001: /* jcifs smb client library in Java
002: * Copyright (C) 2002 "Michael B. Allen" <jcifs at samba dot org>
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package jcifs.http;
020:
021: import java.io.*;
022: import javax.servlet.*;
023: import javax.servlet.http.*;
024: import java.util.*;
025: import java.text.SimpleDateFormat;
026: import java.net.UnknownHostException;
027: import jcifs.*;
028: import jcifs.http.*;
029: import jcifs.smb.*;
030: import jcifs.netbios.NbtAddress;
031: import jcifs.util.MimeMap;
032: import jcifs.util.Base64;
033:
034: /**
035: * This servlet may be used to "browse" the entire hierarchy of resources
036: * on an SMB network like one might with Network Neighborhood or Windows
037: * Explorer. The users credentials with be negotiated using NTLM SSP if
038: * the client is Microsoft Internet Explorer.
039: *
040: * Please read <a href="../../../ntlmhttpauth.html">jCIFS NTLM HTTP Authentication and the Network Explorer Servlet</a>.
041: */
042:
043: public class NetworkExplorer extends HttpServlet {
044:
045: private MimeMap mimeMap;
046: private String style;
047: private NtlmSsp ntlmSsp;
048: private boolean credentialsSupplied;
049: private boolean enableBasic;
050: private boolean insecureBasic;
051: private String realm, defaultDomain;
052:
053: public void init() throws ServletException {
054: InputStream is;
055: StringBuffer sb = new StringBuffer();
056: byte[] buf = new byte[1024];
057: int n;
058: String name;
059:
060: Config.setProperty("jcifs.smb.client.soTimeout", "600000");
061: Config.setProperty("jcifs.smb.client.attrExpirationPeriod",
062: "300000");
063:
064: Enumeration e = getInitParameterNames();
065: while (e.hasMoreElements()) {
066: name = (String) e.nextElement();
067: if (name.startsWith("jcifs.")) {
068: Config.setProperty(name, getInitParameter(name));
069: }
070: }
071:
072: if (Config.getProperty("jcifs.smb.client.username") == null) {
073: ntlmSsp = new NtlmSsp();
074: } else {
075: credentialsSupplied = true;
076: }
077:
078: try {
079: mimeMap = new MimeMap();
080: is = getClass().getClassLoader().getResourceAsStream(
081: "jcifs/http/ne.css");
082: while ((n = is.read(buf)) != -1) {
083: sb.append(new String(buf, 0, n, "ISO8859_1"));
084: }
085: style = sb.toString();
086: } catch (IOException ioe) {
087: throw new ServletException(ioe.getMessage());
088: }
089:
090: enableBasic = Config
091: .getBoolean("jcifs.http.enableBasic", false);
092: insecureBasic = Config.getBoolean("jcifs.http.insecureBasic",
093: false);
094: realm = Config.getProperty("jcifs.http.basicRealm");
095: if (realm == null)
096: realm = "jCIFS";
097: defaultDomain = Config.getProperty("jcifs.smb.client.domain");
098: }
099:
100: protected void doFile(HttpServletRequest req,
101: HttpServletResponse resp, SmbFile file) throws IOException {
102: byte[] buf = new byte[8192];
103: SmbFileInputStream in;
104: ServletOutputStream out;
105: String url, type;
106: int n;
107:
108: in = new SmbFileInputStream(file);
109: out = resp.getOutputStream();
110: url = file.getPath();
111:
112: resp.setContentType("text/plain");
113:
114: if ((n = url.lastIndexOf('.')) > 0
115: && (type = url.substring(n + 1)) != null
116: && type.length() > 1 && type.length() < 6) {
117: resp.setContentType(mimeMap.getMimeType(type));
118: }
119: resp.setHeader("Content-Length", file.length() + "");
120: resp.setHeader("Accept-Ranges", "Bytes");
121:
122: while ((n = in.read(buf)) != -1) {
123: out.write(buf, 0, n);
124: }
125: }
126:
127: protected int compareNames(SmbFile f1, String f1name, SmbFile f2)
128: throws IOException {
129: if (f1.isDirectory() != f2.isDirectory()) {
130: return f1.isDirectory() ? -1 : 1;
131: }
132: return f1name.compareToIgnoreCase(f2.getName());
133: }
134:
135: protected int compareSizes(SmbFile f1, String f1name, SmbFile f2)
136: throws IOException {
137: long diff;
138:
139: if (f1.isDirectory() != f2.isDirectory()) {
140: return f1.isDirectory() ? -1 : 1;
141: }
142: if (f1.isDirectory()) {
143: return f1name.compareToIgnoreCase(f2.getName());
144: }
145: diff = f1.length() - f2.length();
146: if (diff == 0) {
147: return f1name.compareToIgnoreCase(f2.getName());
148: }
149: return diff > 0 ? -1 : 1;
150: }
151:
152: protected int compareTypes(SmbFile f1, String f1name, SmbFile f2)
153: throws IOException {
154: String f2name, t1, t2;
155: int i;
156:
157: if (f1.isDirectory() != f2.isDirectory()) {
158: return f1.isDirectory() ? -1 : 1;
159: }
160: f2name = f2.getName();
161: if (f1.isDirectory()) {
162: return f1name.compareToIgnoreCase(f2name);
163: }
164: i = f1name.lastIndexOf('.');
165: t1 = i == -1 ? "" : f1name.substring(i + 1);
166: i = f2name.lastIndexOf('.');
167: t2 = i == -1 ? "" : f2name.substring(i + 1);
168:
169: i = t1.compareToIgnoreCase(t2);
170: if (i == 0) {
171: return f1name.compareToIgnoreCase(f2name);
172: }
173: return i;
174: }
175:
176: protected int compareDates(SmbFile f1, String f1name, SmbFile f2)
177: throws IOException {
178: if (f1.isDirectory() != f2.isDirectory()) {
179: return f1.isDirectory() ? -1 : 1;
180: }
181: if (f1.isDirectory()) {
182: return f1name.compareToIgnoreCase(f2.getName());
183: }
184: return f1.lastModified() > f2.lastModified() ? -1 : 1;
185: }
186:
187: protected void doDirectory(HttpServletRequest req,
188: HttpServletResponse resp, SmbFile dir) throws IOException {
189: PrintWriter out;
190: SmbFile[] dirents;
191: SmbFile f;
192: int i, j, len, maxLen, dirCount, fileCount, sort;
193: String str, name, path, fmt;
194: LinkedList sorted;
195: ListIterator iter;
196: SimpleDateFormat sdf = new SimpleDateFormat("MM/d/yy h:mm a");
197: GregorianCalendar cal = new GregorianCalendar();
198:
199: sdf.setCalendar(cal);
200:
201: dirents = dir.listFiles();
202: sorted = new LinkedList();
203: if ((fmt = req.getParameter("fmt")) == null) {
204: fmt = "col";
205: }
206: sort = 0;
207: if ((str = req.getParameter("sort")) == null
208: || str.equals("name")) {
209: sort = 0;
210: } else if (str.equals("size")) {
211: sort = 1;
212: } else if (str.equals("type")) {
213: sort = 2;
214: } else if (str.equals("date")) {
215: sort = 3;
216: }
217: dirCount = fileCount = 0;
218: maxLen = 28;
219: for (i = 0; i < dirents.length; i++) {
220: try {
221: if (dirents[i].getType() == SmbFile.TYPE_NAMED_PIPE) {
222: continue;
223: }
224: } catch (SmbAuthException sae) {
225: } catch (SmbException se) {
226: if (se.getNtStatus() != se.NT_STATUS_UNSUCCESSFUL) {
227: throw se;
228: }
229: }
230: if (dirents[i].isDirectory()) {
231: dirCount++;
232: } else {
233: fileCount++;
234: }
235:
236: name = dirents[i].getName();
237: len = name.length();
238: if (len > maxLen) {
239: maxLen = len;
240: }
241:
242: iter = sorted.listIterator();
243: for (j = 0; iter.hasNext(); j++) {
244: if (sort == 0) {
245: if (compareNames(dirents[i], name, (SmbFile) iter
246: .next()) < 0) {
247: break;
248: }
249: } else if (sort == 1) {
250: if (compareSizes(dirents[i], name, (SmbFile) iter
251: .next()) < 0) {
252: break;
253: }
254: } else if (sort == 2) {
255: if (compareTypes(dirents[i], name, (SmbFile) iter
256: .next()) < 0) {
257: break;
258: }
259: } else if (sort == 3) {
260: if (compareDates(dirents[i], name, (SmbFile) iter
261: .next()) < 0) {
262: break;
263: }
264: }
265: }
266: sorted.add(j, dirents[i]);
267: }
268: if (maxLen > 50) {
269: maxLen = 50;
270: }
271: maxLen *= 9; /* convert to px */
272:
273: out = resp.getWriter();
274:
275: resp.setContentType("text/html");
276:
277: out
278: .println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
279: out.println("<html><head><title>Network Explorer</title>");
280: out
281: .println("<meta HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">");
282: out.println("<style TYPE=\"text/css\">");
283:
284: out.println(style);
285:
286: if (dirents.length < 200) {
287: out.println(" a:hover {");
288: out.println(" background: #a2ff01;");
289: out.println(" }");
290: }
291:
292: out.println("</STYLE>");
293: out.println("</head><body>");
294:
295: out.print("<a class=\"sort\" style=\"width: " + maxLen
296: + ";\" href=\"?fmt=detail&sort=name\">Name</a>");
297: out
298: .println("<a class=\"sort\" href=\"?fmt=detail&sort=size\">Size</a>");
299: out
300: .println("<a class=\"sort\" href=\"?fmt=detail&sort=type\">Type</a>");
301: out
302: .println("<a class=\"sort\" style=\"width: 180\" href=\"?fmt=detail&sort=date\">Modified</a><br clear='all'><p>");
303:
304: path = dir.getCanonicalPath();
305:
306: if (path.length() < 7) {
307: out.println("<b><big>smb://</big></b><br>");
308: path = ".";
309: } else {
310: out.println("<b><big>" + path + "</big></b><br>");
311: path = "../";
312: }
313: out.println((dirCount + fileCount) + " objects (" + dirCount
314: + " directories, " + fileCount + " files)<br>");
315: out
316: .println("<b><a class=\"plain\" href=\".\">normal</a> | <a class=\"plain\" href=\"?fmt=detail\">detailed</a></b>");
317: out
318: .println("<p><table border='0' cellspacing='0' cellpadding='0'><tr><td>");
319:
320: out.print("<A style=\"width: " + maxLen);
321: out.print("; height: 18;\" HREF=\"");
322: out.print(path);
323: out.println("\"><b>↑</b></a>");
324: if (fmt.equals("detail")) {
325: out.println("<br clear='all'>");
326: }
327:
328: if (path.length() == 1
329: || dir.getType() != SmbFile.TYPE_WORKGROUP) {
330: path = "";
331: }
332:
333: iter = sorted.listIterator();
334: while (iter.hasNext()) {
335: f = (SmbFile) iter.next();
336: name = f.getName();
337:
338: if (fmt.equals("detail")) {
339: out.print("<A style=\"width: " + maxLen);
340: out.print("; height: 18;\" HREF=\"");
341: out.print(path);
342: out.print(name);
343:
344: if (f.isDirectory()) {
345: out.print("?fmt=detail\"><b>");
346: out.print(name);
347: out.print("</b></a>");
348: } else {
349: out.print("\"><b>");
350: out.print(name);
351: out.print("</b></a><div align='right'>");
352: out.print((f.length() / 1024) + " KB </div><div>");
353: i = name.lastIndexOf('.') + 1;
354: if (i > 1 && (name.length() - i) < 6) {
355: out.print(name.substring(i).toUpperCase()
356: + "</div class='ext'>");
357: } else {
358: out.print(" </div>");
359: }
360: out.print("<div style='width: 180'>");
361: out.print(sdf.format(new Date(f.lastModified())));
362: out.print("</div>");
363: }
364: out.println("<br clear='all'>");
365: } else {
366: out.print("<A style=\"width: " + maxLen);
367: if (f.isDirectory()) {
368: out.print("; height: 18;\" HREF=\"");
369: out.print(path);
370: out.print(name);
371: out.print("\"><b>");
372: out.print(name);
373: out.print("</b></a>");
374: } else {
375: out.print(";\" HREF=\"");
376: out.print(path);
377: out.print(name);
378: out.print("\"><b>");
379: out.print(name);
380: out.print("</b><br><small>");
381: out.print((f.length() / 1024) + "KB <br>");
382: out.print(sdf.format(new Date(f.lastModified())));
383: out.print("</small>");
384: out.println("</a>");
385: }
386: }
387: }
388:
389: out.println("</td></tr></table>");
390: out.println("</BODY></HTML>");
391: out.close();
392: }
393:
394: private String parseServerAndShare(String pathInfo) {
395: char[] out = new char[256];
396: char ch;
397: int len, p, i;
398:
399: if (pathInfo == null) {
400: return null;
401: }
402: len = pathInfo.length();
403:
404: p = i = 0;
405: while (p < len && pathInfo.charAt(p) == '/') {
406: p++;
407: }
408: if (p == len) {
409: return null;
410: }
411:
412: /* collect server name */
413: while (p < len && (ch = pathInfo.charAt(p)) != '/') {
414: out[i++] = ch;
415: p++;
416: }
417: while (p < len && pathInfo.charAt(p) == '/') {
418: p++;
419: }
420: if (p < len) { /* then there must be a share */
421: out[i++] = '/';
422: do { /* collect the share name */
423: out[i++] = (ch = pathInfo.charAt(p++));
424: } while (p < len && ch != '/');
425: }
426: return new String(out, 0, i);
427: }
428:
429: public void doGet(HttpServletRequest req, HttpServletResponse resp)
430: throws IOException, ServletException {
431: UniAddress dc;
432: String msg, pathInfo, server = null;
433: boolean offerBasic, possibleWorkgroup = true;
434: NtlmPasswordAuthentication ntlm = null;
435: HttpSession ssn = req.getSession(false);
436:
437: if ((pathInfo = req.getPathInfo()) != null) {
438: int i;
439: server = parseServerAndShare(pathInfo);
440: if (server != null && (i = server.indexOf('/')) > 0) {
441: server = server.substring(0, i).toLowerCase();
442: possibleWorkgroup = false;
443: }
444: }
445:
446: msg = req.getHeader("Authorization");
447: offerBasic = enableBasic && (insecureBasic || req.isSecure());
448:
449: if (msg != null
450: && (msg.startsWith("NTLM ") || (offerBasic && msg
451: .startsWith("Basic ")))) {
452:
453: if (msg.startsWith("NTLM ")) {
454: byte[] challenge;
455:
456: if (pathInfo == null || server == null) {
457: String mb = NbtAddress.getByName(
458: NbtAddress.MASTER_BROWSER_NAME, 0x01, null)
459: .getHostAddress();
460: dc = UniAddress.getByName(mb);
461: } else {
462: dc = UniAddress
463: .getByName(server, possibleWorkgroup);
464: }
465:
466: req.getSession(); /* ensure session id is set for cluster env. */
467: challenge = SmbSession.getChallenge(dc);
468: if ((ntlm = NtlmSsp.authenticate(req, resp, challenge)) == null) {
469: return;
470: }
471: } else { /* Basic */
472: String auth = new String(Base64
473: .decode(msg.substring(6)), "US-ASCII");
474: int index = auth.indexOf(':');
475: String user = (index != -1) ? auth.substring(0, index)
476: : auth;
477: String password = (index != -1) ? auth
478: .substring(index + 1) : "";
479: index = user.indexOf('\\');
480: if (index == -1)
481: index = user.indexOf('/');
482: String domain = (index != -1) ? user
483: .substring(0, index) : defaultDomain;
484: user = (index != -1) ? user.substring(index + 1) : user;
485: ntlm = new NtlmPasswordAuthentication(domain, user,
486: password);
487: }
488:
489: req.getSession().setAttribute("npa-" + server, ntlm);
490:
491: } else if (!credentialsSupplied) {
492: if (ssn != null) {
493: ntlm = (NtlmPasswordAuthentication) ssn
494: .getAttribute("npa-" + server);
495: }
496: if (ntlm == null) {
497: resp.setHeader("WWW-Authenticate", "NTLM");
498: if (offerBasic) {
499: resp.addHeader("WWW-Authenticate", "Basic realm=\""
500: + realm + "\"");
501: }
502: resp.setHeader("Connection", "close");
503: resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
504: resp.flushBuffer();
505: return;
506: }
507: }
508:
509: try {
510: SmbFile file;
511:
512: if (ntlm != null) {
513: file = new SmbFile("smb:/" + pathInfo, ntlm);
514: } else if (server == null) {
515: file = new SmbFile("smb://");
516: } else {
517: file = new SmbFile("smb:/" + pathInfo);
518: }
519:
520: if (file.isDirectory()) {
521: doDirectory(req, resp, file);
522: } else {
523: doFile(req, resp, file);
524: }
525: } catch (SmbAuthException sae) {
526: if (ssn != null) {
527: ssn.removeAttribute("npa-" + server);
528: }
529: if (sae.getNtStatus() == sae.NT_STATUS_ACCESS_VIOLATION) {
530: /* Server challenge no longer valid for
531: * externally supplied password hashes.
532: */
533: resp.sendRedirect(req.getRequestURL().toString());
534: return;
535: }
536: resp.setHeader("WWW-Authenticate", "NTLM");
537: if (offerBasic) {
538: resp.addHeader("WWW-Authenticate", "Basic realm=\""
539: + realm + "\"");
540: }
541: resp.setHeader("Connection", "close");
542: resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
543: resp.flushBuffer();
544: return;
545: } catch (DfsReferral dr) {
546: StringBuffer redir = req.getRequestURL();
547: String qs = req.getQueryString();
548: redir = new StringBuffer(redir.substring(0, redir.length()
549: - req.getPathInfo().length()));
550: redir.append(dr.node.replace('\\', '/'));
551: redir.append('/');
552: if (qs != null) {
553: redir.append(req.getQueryString());
554: }
555: resp.sendRedirect(redir.toString());
556: resp.flushBuffer();
557: return;
558: }
559: }
560: }
|