1
/** BEGIN COPYRIGHT BLOCK
2
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
3
* Copyright (C) 2005 Red Hat, Inc.
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
* END COPYRIGHT BLOCK **/
20
package com.netscape.admin.dirserv.panel.replication;
22
import java.util.Enumeration;
23
import java.util.Hashtable;
24
import java.util.StringTokenizer;
27
import javax.swing.JOptionPane;
28
import javax.swing.BorderFactory;
29
import javax.swing.JFrame;
30
import javax.swing.AbstractButton;
31
import javax.swing.text.JTextComponent;
32
import javax.swing.border.*;
33
import com.netscape.admin.dirserv.IDSModel;
34
import com.netscape.admin.dirserv.DSUtil;
35
import com.netscape.admin.dirserv.panel.UIFactory;
36
import com.netscape.management.client.console.ConsoleInfo;
37
import com.netscape.management.client.util.Debug;
38
import com.netscape.management.client.util.RemoteImage;
39
import com.netscape.management.client.util.ResourceSet;
40
import netscape.ldap.*;
43
* Image Retrieval Tools for the package
50
public class ReplicationTool {
52
final static int borderWidth = UIFactory.getBorderInsets().left;
53
final static Border EMPTY_BORDER =
54
BorderFactory.createEmptyBorder(borderWidth,borderWidth,
55
borderWidth,borderWidth);
56
final static Border LOWERED_BORDER =
57
BorderFactory.createLoweredBevelBorder();
58
final static Border RAISED_BORDER =
59
BorderFactory.createRaisedBevelBorder();
60
final static Border ETCHED_BORDER = BorderFactory.createEtchedBorder();
61
final static Dimension DEFAULT_PANEL_SIZE = new Dimension(300,300);
62
final static Dimension TALL_PANEL_SIZE = new Dimension(300,440);
64
//copy from BlankPanel
65
final static int DEFAULT_PADDING = 6;
66
final static Insets DEFAULT_EMPTY_INSETS = new Insets(0,0,0,0);
67
final static Insets BOTTOM_INSETS = new Insets(DEFAULT_PADDING,
71
final static Insets DEFAULT_INSETS = new Insets(DEFAULT_PADDING,
76
// Used for getting/setting machine data DN in ConsoleInfo hashtable
77
final static String MD_ENTRY_DN = "cn=replication, cn=config";
78
final static String MD_ATTRIBUTE_KEY = "netscapeMDSuffix.key";
80
// this has package scope, used by other classes in replication
81
final static String REPLICA_CN = "replica";
82
final static String REPLICA_RDN = "cn=" + REPLICA_CN;
84
final static String WINDOWS_REPLICA_CN="replica";
85
final static String WINDOWS_REPLICA_RDN="cn=" + WINDOWS_REPLICA_CN;
87
final static String[] REPLICA_OBJECTCLASSES = {"top", "nsDS5Replica"};
88
final static String REPLICA_ROOT_ATTR = "nsDS5ReplicaRoot";
89
final static String REPLICA_TYPE_ATTR = "nsDS5ReplicaType";
90
final static String REPLICA_BINDDN_ATTR = "nsDS5ReplicaBindDN";
91
final static String PUBLIC_SUFFIX_ATTR = "nsslapd-suffix";
92
final static String MMR_AGREEMENT_OBJECTCLASS = "nsDS5ReplicationAgreement";
93
final static String[] MMR_AGREEMENT_OBJECTCLASSES = {"top", MMR_AGREEMENT_OBJECTCLASS};
95
final static String WIN_AGREEMENT_OBJECTCLASS = "nsDSWindowsReplicationAgreement";
96
final static String[] WIN_AGGREEMENT_OBJECTCLASSES = {"top", WIN_AGREEMENT_OBJECTCLASS};
97
final static String REPLICA_WINDOWS_PRIMARY_HOST_ATTR = "nsDSWindowsReplicaPrimaryHost";
98
final static String REPLICA_WINDOWS_DOMAIN = "nsds7WindowsDomain";
99
final static String REPLICA_WINDOWS_SUBTREE = "nsds7WindowsReplicaSubtree";
100
final static String REPLICA_DS_SUBTREE = "nsds7DirectoryReplicaSubtree";
101
final static String REPLICA_NEW_WIN_USER_SYNC = "nsds7NewWinUserSyncEnabled";
102
final static String REPLICA_NEW_WIN_GROUP_SYNC = "nsds7NewWinGroupSyncEnabled";
103
final static String REPLICA_WINDOWS_ALTERNATE_HOST_ATTR = "nsDSWindowsReplicaAlternateHost";
104
final static String REPLICA_HOST_ATTR = "nsDS5ReplicaHost";
105
final static String REPLICA_PORT_ATTR = "nsDS5ReplicaPort";
106
final static String REPLICA_TRANSPORT_ATTR = "nsDS5ReplicaTransportInfo";
107
final static String REPLICA_TRANSPORT_SSL = "SSL";
108
final static String REPLICA_TRANSPORT_TLS = "TLS";
109
final static String REPLICA_TRANSPORT_LDAP= "LDAP";
110
final static String REPLICA_CRED_ATTR = "nsDS5ReplicaCredentials";
111
final static String REPLICA_BINDMETHOD_ATTR = "nsDS5ReplicaBindMethod";
112
final static String REPLICA_REPLATTRS_ATTR = "nsDS5ReplicatedAttributeList";
113
final static String REPLICA_BINDMETHOD_SIMPLE = "SIMPLE";
114
final static String REPLICA_BINDMETHOD_SSLCLIENTAUTH = "SSLCLIENTAUTH";
115
final static String REPLICA_BINDMETHOD_SASL_GSSAPI = "SASL/GSSAPI";
116
final static String REPLICA_BINDMETHOD_SASL_DIGEST_MD5 = "SASL/DIGEST-MD5";
117
final static String REPLICA_SCHEDULE_ATTR = "nsDS5ReplicaUpdateSchedule";
118
final static String REPLICA_REFRESH_ATTR = "nsds5BeginReplicaRefresh";
119
final static String MMR_NAME_ATTR = "cn";
120
final static String MMR_NICKNAME_ATTR = "description";
121
final static String LEGACYR_AGREEMENT_OBJECTCLASS = "LDAPReplica";
122
final static String[] LEGACYR_AGREEMENT_OBJECTCLASSES = {"top", LEGACYR_AGREEMENT_OBJECTCLASS};
123
final static String REPLICA_CONSUMER_INIT_BEGIN_ATTR = "nsds5replicaLastInitStart";
124
final static String REPLICA_CONSUMER_INIT_END_ATTR = "nsds5replicaLastInitEnd";
125
final static String REPLICA_CONSUMER_INIT_STATUS_ATTR = "nsds5replicalastinitstatus";
126
final static String REPLICA_LAST_UPDATE_START_ATTR = "nsds5replicaLastUpdateStart";
127
final static String REPLICA_LAST_UPDATE_END_ATTR = "nsds5replicaLastUpdateEnd";
128
final static String REPLICA_N_CHANGES_SENT_ATTR = "nsds5replicaChangesSentSinceStartup";
129
final static String REPLICA_LAST_UPDATE_STATUS_ATTR = "nsds5replicaLastUpdateStatus";
130
final static String REPLICA_UPDATE_IN_PROGRESS_ATTR = "nsds5replicaUpdateInProgress";
131
final static String REPLICA_LEGACY_CONSUMER_ATTR = "nsds5ReplicaLegacyConsumer";
132
final static String REPLICA_PURGE_DELAY_ATTR = "nsds5ReplicaPurgeDelay";
133
final static String DEFAULT_PURGE_DELAY = "604800";
134
final static String REPLICA_ID_ATTR = "nsDS5ReplicaID";
135
final static String REPLICA_LOG_CHANGES_ATTR = "nsDS5Flags";
136
final static String REPLICA_REFERRAL_ATTR = "nsDS5ReplicaReferral";
138
final static String REPLICA_CONSUMER_INIT_IN_PROGRESS = "start";
140
final static String[] REPLICA_STATUS_ATTRS = {
141
REPLICA_LAST_UPDATE_START_ATTR, REPLICA_LAST_UPDATE_END_ATTR,
142
REPLICA_N_CHANGES_SENT_ATTR, REPLICA_LAST_UPDATE_STATUS_ATTR,
143
REPLICA_UPDATE_IN_PROGRESS_ATTR, REPLICA_CONSUMER_INIT_BEGIN_ATTR,
144
REPLICA_CONSUMER_INIT_END_ATTR, REPLICA_CONSUMER_INIT_STATUS_ATTR,
148
static RemoteImage getImage( String name ) {
149
RemoteImage i = (RemoteImage)_cPackageImages.get( name );
152
i = new RemoteImage( _sImageDir + "/" + name );
154
_cPackageImages.put( name, i );
158
public static void resetGBC(GridBagConstraints gbc) {
159
gbc.gridx = gbc.RELATIVE;
160
gbc.gridy = gbc.RELATIVE;
163
gbc.fill = gbc.HORIZONTAL;
164
gbc.anchor = gbc.CENTER;
169
gbc.insets = (Insets)DEFAULT_INSETS.clone();
173
* Read the machine data dn from the root DSE
175
static String getMachineDataDN(ConsoleInfo info) {
176
String machineDataDN = null;
178
// ConsoleInfo extends HashTable, so we will just stuff the
179
// Machine Data DN there the first time, and thereafter
181
if ((machineDataDN = (String)info.get(MD_ATTRIBUTE_KEY)) == null) {
182
// We haven't read it yet, so do so, and then save it
184
LDAPConnection ld = info.getLDAPConnection();
186
LDAPEntry entry = null;
188
entry = ld.read(MD_ENTRY_DN);
189
} catch (LDAPException le) {
190
Debug.println("fail read : " + MD_ENTRY_DN + " error:" + le.toString());
193
machineDataDN = entry.getDN();
194
info.put(MD_ATTRIBUTE_KEY, machineDataDN);
198
Debug.println("getMachineDataDN: returning " + machineDataDN);
199
return machineDataDN;
203
static private String lookupRealDSHost(ConsoleInfo info) {
204
LDAPConnection ld = info.getLDAPConnection();
206
String dataversion="";
207
String[] attrList = {"dataversion"};
209
entry = ld.read("", attrList);
210
LDAPAttributeSet attrs = entry.getAttributeSet();
211
Enumeration attrsEnum = (Enumeration)attrs.getAttributes();
212
while (attrsEnum.hasMoreElements()){
213
LDAPAttribute attr = (LDAPAttribute)attrsEnum.nextElement();
214
if (attr.getName().equalsIgnoreCase("dataversion"))
215
dataversion = (String)attr.getStringValues().nextElement();
217
} catch (LDAPException e) {
221
int col = dataversion.indexOf(":");
224
return dataversion.substring(0, col).trim();
228
// Try to "gently" fully-qualify a host name.
229
// Algorithm. Determine the canonical host name, using the
230
// above method, then see if the leftmost component of the
231
// host name is the same as the candidate host name. If
232
// so, then change the hsot name, otherwise leave it alone.
233
// So, if we determine the canonical host name of
234
// "foo" and it turns out to be "bar.mcom.com", leave it
235
// alone. But, it it returns "foo.mcom.com", then
237
static String fullyQualifyHostName(String host) {
238
String retHost = host;
240
int host_len = host.indexOf('.');
242
host_len = host.length();
244
String newHost = DSUtil.canonicalHost(host);
245
if (newHost != null) {
246
int new_host_len = newHost.length();
247
if (host_len > new_host_len)
248
host_len = new_host_len;
249
if (newHost.substring(0, host_len).equalsIgnoreCase(host)) {
253
} catch (UnknownHostException e) {
256
Debug.println("fullyQualifyHostName: return " + retHost);
261
* Get Authentication as DM
264
public static boolean authenticate(IDSModel model) {
265
boolean success = true;
266
while (!verifyDM(model.getServerInfo()) ) {
268
LDAPConnection ld = model.getServerInfo().getLDAPConnection();
269
if (!ld.isConnected()) {
273
String msg =_resource.getString("replication-dialog",
274
"authenticationAsDM");
275
DSUtil.showErrorDialog( model.getFrame(), "error", msg,
276
"replication-dialog" );
277
if (! model.getNewAuthentication(false)) {
282
model.notifyAuthChangeListeners();
286
//verify DM authentication by searching the machine data
287
static boolean verifyDM(ConsoleInfo info) {
289
LDAPConnection ld = info.getLDAPConnection();
291
if (ld.getAuthenticationDN().trim().equals(""))
294
Debug.println( "ReplicationTool.verifyDM: authDN = <" +
295
ld.getAuthenticationDN() + ">, authPassword = <" +
296
ld.getAuthenticationPassword() + ">" );
298
LDAPSearchResults reslist = null;
299
LDAPSearchConstraints searchConstraints;
300
String filter = "(objectclass=*)";
302
reslist = ld.search(getMachineDataDN(info), ld.SCOPE_SUB,
303
filter, null, false);
304
} catch (LDAPException e) {
305
Debug.println( "ReplicationTool.verifyDM: " + e );
306
displayError(e.errorCodeToString());
310
if (reslist.hasMoreElements()) {
313
Debug.println( "ReplicationTool.verifyDM: no results on search" );
319
* LDAP Error Message display
320
* @param code LDAPException code
322
static void displayError( String msg ) {
323
if ( (msg == null) || (msg.equals("")) ) {
324
msg = _resource.getString("replication","err0080");
326
DSUtil.showErrorDialog( null, "error", msg,
327
"replication-dialog" );
330
static void displayError( int code ) {
331
displayError( LDAPException.errorCodeToString( code ) );
334
static void displayError(Component frame, String section,
335
String titleKey, String key,
337
DSUtil.showErrorDialog(frame, titleKey, key, args,
342
* This method converts the value of a replica schedule attribute to
343
* a form suitable for the GUI. See also
344
* synchronize.c:read_schedule_item().
346
* @param sched The value of a replica schedule attribute from
347
* the agreement stored in the directory
348
* @param fromHr The hour part of the time to start replicating.
349
* The length will be set to 0 before any other data
351
* @param fromMin The minutes part of the time to start replicating.
352
* The length will be set to 0 before any other data
354
* @param toHr The hour part of the time to stop replicating.
355
* The length will be set to 0 before any other data
357
* @param toMin The minutes part of the time to stop replicating.
358
* The length will be set to 0 before any other data
360
* @param daysOfWeek A list of the days of the week on which to
361
* replicate. The length will be set to 0 before
362
* any other data is written to it.
363
* @return true if sched contained something valid e.g. at least
366
static boolean parseReplicaSchedule(String sched,
368
StringBuffer fromMin,
371
StringBuffer daysOfWeek) {
372
// check for bogosity
373
if (fromHr == null || fromMin == null || toHr == null ||
374
toMin == null || daysOfWeek == null) {
380
fromMin.setLength(0);
383
daysOfWeek.setLength(0);
386
// if sched is not null, it contains at least a 4 character
387
// fromTime. If a '-' is present, a 4 character toTime follows.
388
// If a ' ' is present, the daysOfWeek string follows.
390
boolean status = false; // true if sched contained something . . .
391
if (sched != null && !sched.equals ("") && sched.charAt(0) == '*') {
392
Debug.println("ReplicationTool.parseReplicaSchedule(): invalid" +
393
" replica schedule [" + sched + "]");
394
sched = "0000-2359" + sched.substring(1);
397
if (sched != null && !sched.equals ("") && sched.length() >= 4) {
398
fromHr.append(sched.substring(0, 2));
399
fromMin.append(sched.substring(2, 4));
401
int dashIndex = sched.indexOf('-');
403
toHr.append(sched.substring(dashIndex+1,
405
toMin.append(sched.substring(dashIndex+3,
412
int spaceIndex = sched.indexOf(' ');
414
daysOfWeek.append(sched.substring(spaceIndex+1).trim());
416
daysOfWeek.append("0123456");
421
fromMin.append("00");
424
daysOfWeek.append("0123456");
430
static String createDateString(JTextComponent startHr,
431
JTextComponent startMin,
432
JTextComponent endHr,
433
JTextComponent endMin,
434
AbstractButton[] buttons) {
435
String dowString = "";
436
String startTime = pad4(startHr.getText(), startMin.getText());
437
String endTime = pad4(endHr.getText(), endMin.getText());
438
for (int i = 0; i < 7; i++) {
439
if (buttons[i].isSelected()) {
440
dowString += Integer.toString(i);
443
return startTime + "-" + endTime + " " + dowString;
446
static String pad4(String hour, String min) {
449
if (hour.length () == 0)
451
else if (hour.length () == 1)
456
if (min.length() == 0)
458
else if (min.length() == 1)
459
return res + "0" + min;
465
* Takes a message with format <Error Code> <Error message> and creates a more user friendly message
467
* If there is an error (error code different than 0) the result is a message of type <Error message>. Code: <Error Code>.
468
* If there is not an error the result is a message of type <Error message>.
470
* If something goes wrong we return the original message.
471
* The following examples correspond to the english version:
472
* Example 1: "0 Incremental Update Succeeded" becomes "Incremental Update Succeeded."
473
* Example 2: "82 Could not connect to LDAP Server" becomes "Could not connect to LDAP Server. Error Code: 82"
475
* @param originalMessage String the original message
476
* @return the message with the new format
478
public static String convertStatusMessage(String originalMessage) {
479
StringTokenizer st = new StringTokenizer(originalMessage, " ");
480
String code = st.nextToken();
481
if ((code != null) &&
482
(code.length() > 0)) {
484
int theCode = Integer.parseInt(code);
485
/* We could parse the token => until here everything is fine! */
486
String errorMessage = originalMessage.substring(code.length() + 1);
487
if ((errorMessage != null) &&
488
(errorMessage.length() > 0)) {
490
String[] args = {errorMessage, code};
491
return _resource.getString("replication", "convertupdatemessage-error-label", args);
496
} catch (Exception ex) {
497
Debug.println("ReplicationTool.convertStatusMessage() exception parsing "+code +": "+ex);
500
return originalMessage;
504
* Tries to determine if the given string is a hashed password. Assumes the hashed password is
505
* in a form like {[a-zA-Z0-9]+} e.g. {crypt} or {SSHA} or {DES}
506
* null and empty strings are considered not hashed
507
* The string must begin with the '{' character, followed by alphanumeric characters
508
* followed by '}' followed by more alphanum to be considered a hashed password
510
* @param pwd The hashed password
511
* @return true if the string is a hashed password, false otherwise
513
public static boolean isHashedPwd(String pwd) {
515
if (pwd != null && pwd.length() > 0) {
516
if (pwd.charAt(0) == '{') {
518
for (ii = 1; ii < pwd.length() &&
519
Character.isLetterOrDigit(pwd.charAt(ii)); ++ii) {
521
// there should be more characters in the string after the \}
522
ret = ((ii+1) < pwd.length() && pwd.charAt(ii) == '}');
528
//get resource bundle
529
private static ResourceSet _resource =
531
"com.netscape.admin.dirserv.panel.replication.replication");
532
private static Hashtable _cPackageImages = new Hashtable();
533
private static String _sImageDir = "com/netscape/admin/dirserv/images";