001: /*
002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
004: */
005: package com.sun.im.portal.provider;
006:
007: import java.io.*;
008: import java.net.*;
009: import java.util.*;
010: import java.util.logging.Logger;
011: import java.util.logging.Level;
012: import javax.servlet.http.*;
013: import java.security.AccessController;
014: import com.sun.portal.providers.Provider;
015: import com.sun.portal.providers.ProviderException;
016: import com.sun.portal.providers.jsp.JSPProvider;
017: import com.sun.portal.log.common.PortalLogger;
018: import com.sun.im.service.*;
019: import com.sun.identity.security.EncryptAction;
020: import com.sun.identity.security.DecryptAction;
021: import com.iplanet.am.sdk.*;
022: import com.iplanet.am.util.*;
023: import com.iplanet.sso.*;
024: import com.sun.im.portal.util.NetletRule;
025:
026: public class IMProvider extends JSPProvider {
027: static public final String SESSION_FACTORY = "com.iplanet.im.client.api.TransUnidirectionalSessionProvider";
028: //static public final String SESSION_FACTORY = "com.iplanet.im.client.api.iIMUnidirectionalSessionFactory";
029: //static private final String SESSION_FACTORY = "com.iplanet.im.client.api.iIMSessionFactory";
030: static private CollaborationSessionFactory factory;
031:
032: // Default values should let the admin know something is misconfigured, along with the logs that
033: // will explain the problem when parsing the netlet rule
034: private int secureIMPort = 99999;
035: private int secureDownloadPort = 99999;
036:
037: // Create a logger for this class
038: private static Logger debugLogger = PortalLogger
039: .getLogger("com.sun.portal.im.provider");
040:
041: public void init(String n, HttpServletRequest httpreq)
042: throws ProviderException {
043: super .init(n, httpreq);
044: synchronized (this .getClass()) {
045: try {
046: if (factory == null) {
047: //System.setProperty("com.sun.im.xmpp.log", "debug");
048: factory = new CollaborationSessionFactory(
049: SESSION_FACTORY);
050: }
051: } catch (Exception e) {
052: throw new ProviderException(
053: "IMProvider.init(): Problem creating CollarborationSessionFactory",
054: e);
055: }
056: }
057: }
058:
059: // Method to build and return the content required to launch the client or start a conference
060: public StringBuffer getLaunchContent(HttpServletRequest req,
061: HttpServletResponse res) throws ProviderException {
062: debugLogger.finest("IMProvider.getLaunchContent: start");
063:
064: // Check if this launch is to start a conference with another user
065: String username = req.getParameter("username");
066:
067: // Check if the user is online already. If so go ahead and invite, pull in the invite page
068: try {
069: CollaborationSession session = getCollaborationSession(req);
070:
071: if (session == null)
072: throw new Exception(
073: "Collaboration Session returned null");
074: debugLogger
075: .log(Level.FINEST,
076: "IMProvider.getLaunchContent: Have a collaboration session");
077:
078: String me = session.getPrincipal().getUID();
079: debugLogger
080: .log(
081: Level.FINEST,
082: "IMProvider.getLaunchContent: Result of getPrincipal is {0}",
083: me);
084: PresenceSession ps = (PresenceSession) session
085: .accessService(CollaborationSessionFactory.PRESENCE);
086: // Workaround for self-presence failures for AM SSOToken logins
087: PresenceTuple pt = new PresenceTuple(me);
088: pt.setStatus(PresenceSession.STATUS_OPEN);
089: pt.setPriority(0);
090: Presence presence = new Presence(me);
091: presence.addTuple(pt);
092: ps.publish(presence.toString());
093: debugLogger
094: .log(Level.FINEST,
095: "IMProvider.getLaunchContent: Published open presence tuple");
096: pt.setStatus(PresenceSession.STATUS_CLOSED);
097: pt.setPriority(0);
098: presence = new Presence(me);
099: presence.addTuple(pt);
100: ps.publish(presence.toString());
101: // End workaround
102: String pi = ps.fetch(me);
103: debugLogger
104: .log(
105: Level.FINEST,
106: "IMProvider.getLaunchContent: Result of fetching user from presenceSession is {0}",
107: pi);
108: PresenceHelper ph = new PresenceHelper(pi);
109:
110: // Check all tuples
111: Iterator i = ph.getTuples().iterator();
112: debugLogger
113: .log(Level.FINEST,
114: "IMProvider.getLaunchContent: Got presencetuple iterator");
115: while (i.hasNext()) {
116: PresenceTuple t = (PresenceTuple) i.next();
117: debugLogger
118: .log(
119: Level.FINEST,
120: "IMProvider.getLaunchContent: Testing presencetuple {0}",
121: t);
122: debugLogger.log(Level.FINEST,
123: "IMProvider.getLaunchContent: Status is {0}", t
124: .getStatus());
125: if (!(t.getStatus().equals(
126: PresenceSession.STATUS_CLOSED) || t.getStatus()
127: .equals(PresenceSession.STATUS_FORWARDED))) {
128: debugLogger
129: .log(
130: Level.FINEST,
131: "IMProvider.getLaunchContent: PresenceTuple is not closed or forwarded, user is online");
132: // Check if there is a guest to start a conference.
133: if (username == null) {
134: // Launching a new client
135: debugLogger
136: .log(
137: Level.FINEST,
138: "IMProvider.getLaunchContent: User already online but wants to launch a new client");
139: break;
140: // No guest selected, set attribute to redirect to already on line page
141: //debugLogger.log(Level.FINEST, "IMProvider.getLaunchContent: Redirecting to alreadyOnline.jsp");
142: //session.logout();
143: //req.setAttribute("AlreadyOnlineRedirect","true");
144: //return super.getContent(req, res);
145: }
146: // Guest selected - ahead and invite, set attribute to redirect to invite page
147: String msg = req.getParameter("msg");
148: if (msg == null)
149: msg = ""; // Never pass a null msg to the IM SDK
150: debugLogger
151: .log(
152: Level.FINEST,
153: "IMProvider.getLaunchContent: Starting the conference with {0}",
154: username);
155: startConference(session, me, username, msg);
156: session.logout();
157: debugLogger
158: .log(Level.FINEST,
159: "IMProvider.getLaunchContent: Redirecting to invite.jsp");
160: req.setAttribute("invite", "true");
161: return super .getContent(req, res);
162: } else {
163: debugLogger
164: .log(Level.FINEST,
165: "IMProvider.getLaunchContent: PresenceTuple is closed or forwarded");
166: }
167: }
168: session.logout();
169: } catch (CollaborationException ce) {
170: debugLogger.log(Level.INFO, "PSIC_CSIPP0001", ce);
171: } catch (Exception e) {
172: debugLogger.log(Level.INFO, "PSIC_CSIPP0002", e);
173: }
174:
175: // User isn't online or wants to launch a new client anyhow
176: debugLogger.log(Level.FINEST,
177: "IMProvider.getLaunchContent: Launching new client");
178: // Test if the codebase points to the proper jsp. If the jsp doesn't exist,
179: // the server may be an older version. In that case do the launching.
180: // If the jsp does exist, suck in the result and return that.
181: try {
182: // Encoding is used in multiple locations
183: String encoding = res.getCharacterEncoding();
184: // Codebase is absolutely required
185: String codebase = getStringProperty("codebase");
186: if ((codebase == null) || (codebase.length() == 0)) {
187: // Create a best guess codebase. The IM server deploys the im webapp to
188: // the webcontainer as /im
189: String host = req.getServerName();
190: int port = req.getServerPort();
191: String proto = req.getScheme();
192: URL cburl = new URL(proto, host, port, "/im");
193: codebase = cburl.toString();
194: debugLogger
195: .log(
196: Level.WARNING,
197: "IMProvider.getLaunchContent: Codebase is not defined, using best guess {0}.",
198: codebase);
199: }
200: StringBuffer launchURLSB = new StringBuffer(codebase);
201: if (launchURLSB.charAt(launchURLSB.length() - 1) != '/') {
202: launchURLSB.append('/');
203: }
204: launchURLSB.append("IMLaunch.jsp?");
205: // Add query string here from IMContent.jsp
206:
207: // Check for if the request came thru a gateway
208: String gateway_url = req.getParameter("gateway_url");
209: if (gateway_url != null) {
210: debugLogger
211: .log(Level.FINEST,
212: "IMProvider.getLaunchContent: Building gateway launch URL");
213: // Set values for codebase, server, and gateway url
214: // Pull the netlet rule
215: parseNetletRule(req);
216: // Codebase will be set to use a port on the user's machine running netlet
217: launchURLSB.append("codebase=");
218: URL dummyURL = new URL("http://none/");
219: // This will fill in whatever codebase may be missing WRT URL pieces
220: URL cburl = new URL(dummyURL, codebase);
221: // Use the protocol of the codebase variable
222: String scheme = cburl.getProtocol();
223: launchURLSB.append(URLEncoder.encode(cburl
224: .getProtocol(), encoding));
225: launchURLSB.append(URLEncoder.encode("://localhost:",
226: encoding));
227: launchURLSB.append(secureDownloadPort);
228: launchURLSB.append(URLEncoder.encode(cburl.getPath(),
229: encoding));
230: // Server cannot be null when going thru a gateway
231: // It will be localhost:netlet port
232: launchURLSB.append("&server=");
233: launchURLSB.append(URLEncoder.encode("localhost:",
234: encoding));
235: launchURLSB.append(secureIMPort);
236: // Gateway URL - for some reason the new IM launcher uses it.
237: // May be able to remove this someday
238: launchURLSB.append("&gateway_url=");
239: launchURLSB.append(URLEncoder.encode(gateway_url,
240: encoding));
241: } else {
242: debugLogger
243: .log(Level.FINEST,
244: "IMProvider.getLaunchContent: Building direct launch URL");
245: // Set values for codebase and server
246: launchURLSB.append("codebase=");
247: launchURLSB.append(URLEncoder
248: .encode(codebase, encoding));
249: String server = getIMServer();
250: // If this is coming thru a gateway this will be localhost:netlet port
251: // Otherwise server is the combination of the mux and muxport attributes with a colon in between
252: // The launcher can provide this if the user doesn't
253: if (server != null) {
254: launchURLSB.append("&server=");
255: launchURLSB.append(URLEncoder.encode(server,
256: encoding));
257: }
258:
259: }
260:
261: // launch is the parameter keyword to launch the IMClient. Since in the app channel this can
262: // differ from the users setting of "clientRunMode" set it to the parameter "launch" value
263: // This should never be null if the user gets to this method. It also should be either jnlp or plugin.
264: String type = req.getParameter("launch");
265: launchURLSB.append("&type=");
266: launchURLSB.append(URLEncoder.encode(type, encoding));
267:
268: // UID is needed and should be available
269: launchURLSB.append("&uid=");
270: launchURLSB.append(URLEncoder.encode(getUsername(),
271: encoding));
272:
273: // Token is needed and should be available
274: launchURLSB.append("&token=");
275: launchURLSB.append(URLEncoder.encode(getToken(req),
276: encoding));
277:
278: // Username to invite upon client launch. Message is not supported.
279: if (username != null) {
280: launchURLSB.append("&autoinvite=");
281: launchURLSB.append(URLEncoder
282: .encode(username, encoding));
283: }
284:
285: // Locale should be available
286: String localeString = getProviderContext()
287: .getLocaleString();
288: if ((localeString != null) && (localeString.length() > 0)) {
289: launchURLSB.append("&locale=");
290: launchURLSB.append(URLEncoder.encode(localeString,
291: encoding));
292: }
293:
294: URL launchURL = new URL(launchURLSB.toString());
295: debugLogger
296: .log(
297: Level.FINEST,
298: "IMProvider.getLaunchContent: Finished creating url: {0}",
299: launchURL.toString());
300: HttpURLConnection huc = (HttpURLConnection) launchURL
301: .openConnection();
302: int resCode = huc.getResponseCode();
303: // Response codes that are 2xx are considered successful
304: if ((resCode < 200) || (resCode >= 300)) {
305: throw new Exception("Bad Response from test URL");
306: }
307: // Pass it all back to the response
308: //ServletResponse servletRes = pageContext.getResponse();
309: //servletRes.setContentType(huc.getContentType());
310: //res.setContentType(huc.getContentType());
311: //response.setEncoding(huc.getContentEncoding());
312: //response.setLocale(huc.getLocale());
313: StringBuffer retval = new StringBuffer();
314: InputStream in = huc.getInputStream();
315: // Probably incredibly inefficient
316: BufferedReader br = new BufferedReader(
317: new InputStreamReader(in));
318: String line;
319: debugLogger
320: .log(Level.FINEST,
321: "IMProvider.getLaunchContent: Reading input stream");
322: while ((line = br.readLine()) != null) {
323: retval.append(line);
324: retval.append('\n');
325: }
326: debugLogger
327: .log(Level.FINEST,
328: "IMProvider.getLaunchContent: Finished reading to StringBuffer");
329: return retval;
330: } catch (Exception e) {
331: // Original logic
332: debugLogger
333: .log(
334: Level.WARNING,
335: "IMProvider.getLaunchContent: exception {0} in polling imlauncher, using old launcher code.",
336: e.toString());
337: return super .getContent(req, res);
338: }
339: }
340:
341: public StringBuffer getContent(HttpServletRequest req,
342: HttpServletResponse res) throws ProviderException {
343: debugLogger.finest("IMProvider.getContent: start");
344: // Check to see if this is a launch request - should only come from LaunchServlet until providers can
345: // set content type
346: String launchParam = req.getParameter("launch");
347: if (launchParam != null) {
348: debugLogger.log(Level.FINEST,
349: "IMProvider.getContent: Launch type {0} detected",
350: launchParam);
351: return getLaunchContent(req, res);
352: }
353: return super .getContent(req, res);
354: }
355:
356: public URL processEdit(HttpServletRequest req,
357: HttpServletResponse res) throws ProviderException {
358: String val;
359:
360: /* contactGroup */
361: val = req.getParameter("contactGroup");
362: if (val != null && val.length() > 0) {
363: if (val.equals("__ALL__")) {
364: val = "";
365: }
366: if (!val.equals(getStringProperty("contactGroup"))) {
367: setStringProperty("contactGroup", val);
368: }
369: }
370:
371: /* mux */
372: val = req.getParameter("mux");
373: if (val != null && val.length() > 0
374: && !val.equals(getStringProperty("mux"))) {
375: setStringProperty("mux", val);
376: }
377:
378: /* muxport */
379: val = req.getParameter("muxport");
380: if (val != null && val.length() > 0
381: && !val.equals(getStringProperty("muxport"))) {
382: setStringProperty("muxport", val);
383: }
384:
385: /* clientRunMode */
386: val = req.getParameter("clientRunMode");
387: if (val != null && (val.equals("plugin") || val.equals("jnlp"))
388: && !val.equals(getStringProperty("clientRunMode"))) {
389: setStringProperty("clientRunMode", val);
390: }
391:
392: /* username */
393: val = req.getParameter("username");
394: if (val != null && !val.equals(getStringProperty("username"))) {
395: setStringProperty("username", val);
396: }
397:
398: /* password */
399: val = req.getParameter("password");
400: if (val != null && !val.equals(getDummyPassword())) {
401: if (!val.equals("")) {
402: val = (String) AccessController
403: .doPrivileged(new EncryptAction(val));
404: }
405: setStringProperty("password", val);
406: }
407: return null;
408: }
409:
410: public String getDummyPassword() throws ProviderException {
411: StringBuffer dummy = new StringBuffer();
412: int len = ((String) AccessController
413: .doPrivileged(new DecryptAction(
414: getStringProperty("password")))).length();
415: for (int i = 0; i < len; i++) {
416: dummy.append("*");
417: }
418:
419: return dummy.toString();
420: }
421:
422: public CollaborationSession getCollaborationSession(
423: HttpServletRequest request) throws CollaborationException,
424: Exception, ProviderException {
425:
426: String host = getIMServer();
427: // Don't allow a null host into the factory, it chokes
428: if (host == null)
429: return null;
430: String username = getUsername();
431: String password = getToken(request);
432:
433: return factory.getSession(host.toString(), username, password,
434: IMListener.getInstance());
435: }
436:
437: public String getUsername() throws ProviderException {
438: String username = null;
439: String authMethod = getStringProperty("authMethod");
440: if (authMethod.equals("idsvr")) {
441: username = getStringAttribute(getStringProperty("authUsernameAttr"));
442: } else if (authMethod.equals("ldap")) {
443: username = getStringProperty("username");
444: } else {
445: throw new ProviderException("invalid authMethod: "
446: + authMethod);
447: }
448: return username;
449: }
450:
451: public String getToken(HttpServletRequest request)
452: throws ProviderException {
453: try {
454: String token;
455: String authMethod = getStringProperty("authMethod");
456: if (authMethod.equals("idsvr")) {
457: token = SSOTokenManager.getInstance().createSSOToken(
458: request).getTokenID().toString();
459: } else if (authMethod.equals("ldap")) {
460: String pw = getStringProperty("password");
461: if (pw == null || pw.length() == 0) {
462: token = pw;
463: } else {
464: token = (String) AccessController
465: .doPrivileged(new DecryptAction(pw));
466: }
467: } else {
468: throw new ProviderException("invalid authMethod: "
469: + authMethod);
470: }
471: return token;
472: } catch (Exception e) {
473: throw new ProviderException("IMProvider.getToken", e);
474: }
475: }
476:
477: private void startConference(CollaborationSession session,
478: String initiator, String invitee, String msg)
479: throws ProviderException {
480: try {
481: ConferenceSession cs = (ConferenceSession) session
482: .accessService(CollaborationSessionFactory.CONFERENCE);
483: Conference conf = cs.setupConference(IMListener
484: .getInstance(), Conference.MANAGE);
485:
486: Message inviteMessage = conf.createInviteMessage();
487: MessagePart part = inviteMessage.newPart();
488: inviteMessage.setOriginator(initiator);
489: part.setContent(msg);
490: inviteMessage.addPart(part);
491:
492: inviteMessage.addRecipient(initiator);
493: inviteMessage.addRecipient(invitee);
494: conf.invite(Conference.MANAGE, inviteMessage, IMListener
495: .getInstance());
496: } catch (CollaborationException ce) {
497: throw new ProviderException("IMProvider.startConference:",
498: ce);
499: }
500: }
501:
502: protected String getIMServer() throws ProviderException {
503: String server = getStringProperty("mux");
504: String port = getStringProperty("muxport");
505:
506: if ((server == null) || (server.length() == 0)) {
507: debugLogger.fine("PSIC_CSIPP0003");
508: return null;
509: }
510:
511: try {
512: if ((port == null) || (Integer.parseInt(port) < 1)) {
513: debugLogger.log(Level.WARNING,
514: "Multiplexor port value {0} is invalid", port);
515: return null;
516: }
517: } catch (NumberFormatException nfe) {
518: debugLogger.log(Level.WARNING,
519: "Multiplexor port value {0} is invalid", port);
520: return null;
521: }
522:
523: StringBuffer host = new StringBuffer();
524: host.append(server).append(":").append(port);
525:
526: return host.toString();
527:
528: }
529:
530: // Netlet parser
531: private void parseNetletRule(HttpServletRequest request)
532: throws ProviderException {
533:
534: debugLogger.log(Level.FINEST,
535: "IMProvider.parseNetletRule: Start");
536: try {
537:
538: SSOToken token = SSOTokenManager.getInstance()
539: .createSSOToken(request);
540: AMStoreConnection connection = new AMStoreConnection(token);
541: AMUser user = connection.getUser(token.getPrincipal()
542: .getName());
543: Set rules = user.getAttribute(NetletRule.NETLET_RULE);
544: String ruleName = getStringProperty("netletRule");
545: if ((ruleName == null) || (ruleName.length() == 0)) {
546: throw new ProviderException(
547: "Netlet Rule has not been defined for this user");
548: }
549: String strRule = "";
550: boolean found = false;
551: // The Netlet is stored as a series of strings seperated by pipe characters '|'
552: // 1st value = The rule name, possibly appended with ^ALGO^{encryption algorithm}
553: // 2nd value = ? usually null
554: // 3rd value = download info - if value is true use the sunPortalNetletClientLoopbackPort
555: // user attribute for default netlet loopback port otherwise parse for port #
556: // browser connection:internal webserver name:internal webserver port
557: // 4th value = server port translation - should be single value but apparently can have : in it
558: for (Iterator i = rules.iterator(); i.hasNext();) {
559: strRule = (String) i.next();
560: debugLogger.log(Level.FINEST, "Checking rule {0}",
561: strRule);
562: StringTokenizer st = new StringTokenizer(strRule, "|");
563: if (st.hasMoreElements()) {
564: String nameWithAlgo = st.nextToken();
565: StringTokenizer stName = new StringTokenizer(
566: nameWithAlgo, "^");
567: if (stName.hasMoreElements()) {
568: if (ruleName.equals(stName.nextToken())) {
569: found = true;
570: // This is the rule, do the magic
571: try {
572: st.nextToken(); // take off 2nd value - null
573: // IM download port translation
574: String downloadinfo = st.nextToken(); // 3rd value - download info
575: if (downloadinfo.equals("true")) {
576: String sdp = user
577: .getStringAttribute(NetletRule.NETLET_DEFAULT_PORT);
578: debugLogger
579: .log(
580: Level.FINEST,
581: "IMProvider.parseNetletRule: Using default netlet port {0} for downloader",
582: sdp);
583: secureDownloadPort = Integer
584: .parseInt(sdp);
585: } else {
586: StringTokenizer st1 = new StringTokenizer(
587: downloadinfo, ":");
588: String sdp = st1.nextToken();
589: debugLogger
590: .log(
591: Level.FINEST,
592: "IMProvider.parseNetletRule: Found download port {0}",
593: sdp);
594: secureDownloadPort = Integer
595: .parseInt(sdp);
596: }
597: // IM applet/jnlp download port translation (codebase)
598: String ruleinfo = st.nextToken(); // 4th value - server port translation
599: StringTokenizer st2 = new StringTokenizer(
600: ruleinfo, ":");
601: String sip = st2.nextToken();
602: debugLogger
603: .log(
604: Level.FINEST,
605: "IMProvider.parseNetletRule: Found IM port {0}",
606: sip);
607: secureIMPort = Integer.parseInt(sip);
608: break;
609: } catch (NoSuchElementException nsee) {
610: throw new ProviderException(
611: "Invalid Netlet rule format ("
612: + strRule
613: + ") in channel "
614: + getName());
615: } catch (NumberFormatException nfe) {
616: throw new ProviderException(
617: "Invalid Netlet rule number format ("
618: + strRule
619: + ") in channel "
620: + getName());
621: }
622: }
623: }
624: }
625: }
626: if (!found) {
627: throw new ProviderException("Undefined netletRule ("
628: + ruleName + ") in channel " + getName());
629: }
630: } catch (Exception e) {
631: throw new ProviderException("IMProvider.parseNetletRule:",
632: e);
633: }
634:
635: }
636:
637: }
|