001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.quercus.lib.file;
031:
032: import com.caucho.quercus.QuercusModuleException;
033: import com.caucho.quercus.UnimplementedException;
034: import com.caucho.quercus.annotation.NotNull;
035: import com.caucho.quercus.annotation.Optional;
036: import com.caucho.quercus.annotation.Reference;
037: import com.caucho.quercus.env.*;
038: import com.caucho.quercus.module.AbstractQuercusModule;
039: import com.caucho.quercus.resources.StreamContextResource;
040: import com.caucho.util.L10N;
041: import com.caucho.vfs.TempBuffer;
042:
043: import java.io.IOException;
044: import java.net.Socket;
045: import java.net.UnknownHostException;
046: import java.util.HashMap;
047: import java.util.Map;
048: import java.util.logging.Logger;
049:
050: import javax.net.SocketFactory;
051: import javax.net.ssl.SSLSocket;
052: import javax.net.ssl.SSLSocketFactory;
053:
054: /**
055: * Handling the PHP Stream API
056: */
057: public class StreamModule extends AbstractQuercusModule {
058: private static final L10N L = new L10N(StreamModule.class);
059: private static final Logger log = Logger
060: .getLogger(StreamModule.class.getName());
061:
062: public static final int STREAM_FILTER_READ = 1;
063: public static final int STREAM_FILTER_WRITE = 2;
064: public static final int STREAM_FILTER_ALL = 3;
065:
066: public static final int PSFS_PASS_ON = 2;
067: public static final int PSFS_FEED_ME = 1;
068: public static final int PSFS_ERR_FATAL = 0;
069:
070: public static final int STREAM_USE_PATH = 1;
071: public static final int STREAM_REPORT_ERRORS = 8;
072:
073: public static final int STREAM_CLIENT_ASYNC_CONNECT = 2;
074: public static final int STREAM_CLIENT_CONNECT = 4;
075: public static final int STREAM_CLIENT_PERSISTENT = 1;
076:
077: public static final int STREAM_SERVER_BIND = 4;
078: public static final int STREAM_SERVER_LISTEN = 8;
079:
080: public static final int STREAM_URL_STAT_LINK = 1;
081: public static final int STREAM_URL_STAT_QUIET = 2;
082:
083: private static final HashMap<String, Value> _constMap = new HashMap<String, Value>();
084:
085: private static final HashMap<String, ProtocolWrapper> _wrapperMap = new HashMap<String, ProtocolWrapper>();
086:
087: private static final HashMap<String, ProtocolWrapper> _unregisteredWrapperMap = new HashMap<String, ProtocolWrapper>();
088:
089: private static final ArrayValue _wrapperArray = new ArrayValueImpl();
090:
091: /**
092: * Adds the constant to the PHP engine's constant map.
093: *
094: * @return the new constant chain
095: */
096: public Map<String, Value> getConstMap() {
097: return _constMap;
098: }
099:
100: /*
101: public static void stream_bucket_append(Env env,
102: @NotNull StreamBucketBrigade brigade,
103: @NotNull StreamBucket bucket)
104: {
105: brigade.append(bucket);
106: }
107:
108: @ReturnNullAsFalse
109: public static Value stream_bucket_make_writable(Env env,
110: @NotNull StreamBucketBrigade brigade)
111: {
112: return brigade.popTop();
113: }
114: */
115:
116: /**
117: * Creates a stream context.
118: */
119: public static Value stream_context_create(Env env, @Optional
120: ArrayValue options) {
121: return new StreamContextResource(options);
122: }
123:
124: /**
125: * Returns the options from a stream context.
126: */
127: public static Value stream_context_get_options(Env env,
128: Value resource) {
129: if (resource instanceof StreamContextResource) {
130: return ((StreamContextResource) resource).getOptions();
131: } else {
132: env.warning(L.l("expected resource at '{0}'", resource));
133:
134: return BooleanValue.FALSE;
135: }
136: }
137:
138: /**
139: * Returns the default stream context.
140: */
141: public static Value stream_context_get_default(Env env, @Optional
142: ArrayValue options) {
143: StreamContextResource context = env.getDefaultStreamContext();
144:
145: if (options != null)
146: context.setOptions(options);
147:
148: return context;
149: }
150:
151: /**
152: * Set an options for a stream context.
153: */
154: public static boolean stream_context_set_option(Env env,
155: Value resource, String wrapper, String option, Value value) {
156: if (resource instanceof StreamContextResource) {
157: StreamContextResource context = (StreamContextResource) resource;
158:
159: context.setOption(wrapper, option, value);
160:
161: return true;
162: } else {
163: env.warning(L.l("expected resource at '{0}'", resource));
164:
165: return false;
166: }
167: }
168:
169: /**
170: * Sets parameters for the context
171: */
172: public static boolean stream_context_set_params(Env env,
173: Value resource, ArrayValue value) {
174: if (resource instanceof StreamContextResource) {
175: StreamContextResource context = (StreamContextResource) resource;
176:
177: context.setParameters(value);
178:
179: return true;
180: } else {
181: env.warning(L.l("expected resource at '{0}'", resource));
182:
183: return false;
184: }
185: }
186:
187: /**
188: * Copies from an input stream to an output stream
189: */
190: public static long stream_copy_to_stream(Env env, @NotNull
191: BinaryInput in, @NotNull
192: BinaryOutput out, @Optional("-1")
193: int length, @Optional
194: int offset) {
195: try {
196: if (in == null)
197: return -1;
198:
199: if (out == null)
200: return -1;
201:
202: TempBuffer temp = TempBuffer.allocate();
203: byte[] buffer = temp.getBuffer();
204:
205: while (offset-- > 0)
206: in.read();
207:
208: if (length < 0)
209: length = Integer.MAX_VALUE;
210:
211: long bytesWritten = 0;
212:
213: while (length > 0) {
214: int sublen = buffer.length;
215:
216: if (length < sublen)
217: sublen = (int) length;
218:
219: sublen = in.read(buffer, 0, sublen);
220: if (sublen < 0)
221: return bytesWritten;
222:
223: out.write(buffer, 0, sublen);
224:
225: bytesWritten += sublen;
226: length -= sublen;
227: }
228:
229: TempBuffer.free(temp);
230:
231: return bytesWritten;
232: } catch (IOException e) {
233: throw new QuercusModuleException(e);
234: }
235: }
236:
237: /**
238: * Returns the rest of the file as a string
239: *
240: * @param filename the file's name
241: * @param useIncludePath if true, use the include path
242: * @param context the resource context
243: */
244: public static Value stream_get_contents(Env env, @NotNull
245: BinaryInput in, @Optional("-1")
246: long maxLen, @Optional
247: long offset) {
248: try {
249: if (in == null)
250: return BooleanValue.FALSE;
251:
252: StringBuilder sb = new StringBuilder();
253:
254: int ch;
255:
256: if (maxLen < 0)
257: maxLen = Integer.MAX_VALUE;
258:
259: while (offset-- > 0)
260: in.read();
261:
262: while (maxLen-- > 0 && (ch = in.read()) >= 0) {
263: sb.append((char) ch);
264: }
265:
266: // XXX: handle offset and maxlen
267:
268: return env.createString(sb.toString());
269: } catch (IOException e) {
270: throw new QuercusModuleException(e);
271: }
272: }
273:
274: /**
275: * Returns the next line
276: */
277: public static Value stream_get_line(Env env, @NotNull
278: BinaryInput file, @Optional("-1")
279: long length) {
280: try {
281: if (file == null)
282: return BooleanValue.FALSE;
283:
284: if (length < 0)
285: length = Integer.MAX_VALUE;
286:
287: StringValue line = file.readLine(length);
288:
289: if (line == null)
290: return BooleanValue.FALSE;
291:
292: int lineLength = line.length();
293: if (lineLength == 0)
294: return line;
295:
296: char tail = line.charAt(lineLength - 1);
297:
298: if (tail == '\n')
299: return line.substring(0, line.length() - 1);
300: else if (tail == '\r')
301: return line.substring(0, line.length() - 1);
302: else
303: return line;
304: } catch (IOException e) {
305: throw new QuercusModuleException(e);
306: }
307: }
308:
309: /**
310: * Returns the metadata of this stream.
311: *
312: * XXX: TODO
313: */
314: public static Value stream_get_meta_data(Env env,
315: BinaryStream stream) {
316: if (stream == null)
317: return BooleanValue.FALSE;
318:
319: env.stub("stream_get_meta_data");
320:
321: ArrayValue array = new ArrayValueImpl();
322: array.put(env.createString("timed_out"), BooleanValue.FALSE);
323:
324: return array;
325: }
326:
327: /**
328: * Returns the available transports.
329: */
330: public static Value stream_get_transports(Env env) {
331: ArrayValue value = new ArrayValueImpl();
332:
333: value.append(env.createString("tcp"));
334: value.append(env.createString("udp"));
335:
336: return value;
337: }
338:
339: /**
340: * Returns the available wrappers.
341: */
342: public static Value stream_get_wrappers(Env env) {
343: return _wrapperArray;
344: }
345:
346: public static boolean stream_register_wrapper(Env env,
347: StringValue protocol, String className) {
348: return stream_wrapper_register(env, protocol, className);
349: }
350:
351: /**
352: * stream_set_blocking is stubbed since Quercus should wait for
353: * any stream (unless a non-web-server Quercus is developed.)
354: */
355: public static boolean stream_set_blocking(Env env, @NotNull
356: Value stream, int mode) {
357: env.stub("stream_set_blocking()");
358:
359: if (stream == null)
360: return false;
361: else
362: return true;
363: }
364:
365: public static boolean stream_set_timeout(Env env, @NotNull
366: Value stream, int seconds, @Optional("-1")
367: int milliseconds) {
368: if (stream == null)
369: return false;
370:
371: Object obj = stream.toJavaObject();
372:
373: if (obj instanceof AbstractBinaryInputOutput)
374: ((AbstractBinaryInputOutput) obj)
375: .setTimeout(1000L * seconds);
376:
377: return true;
378: }
379:
380: /**
381: * Sets the write buffer.
382: */
383: public static int stream_set_write_buffer(Env env,
384: BinaryOutput stream, int bufferSize) {
385: return 0;
386: }
387:
388: /*
389: * Opens an Internet connection.
390: */
391: public static Value stream_socket_client(Env env, @NotNull
392: String remoteSocket, @Optional
393: @Reference
394: Value errorInt, @Optional
395: @Reference
396: Value errorStr, @Optional("120.0")
397: double timeout, @Optional("STREAM_CLIENT_CONNECT")
398: int flags, @Optional
399: StreamContextResource context) {
400: try {
401: if (remoteSocket == null) {
402: env.warning("socket to connect to must not be null");
403: return BooleanValue.FALSE;
404: }
405:
406: if (flags != STREAM_CLIENT_CONNECT) {
407: env.stub("unsupported stream_socket_client flag");
408: }
409:
410: boolean isSecure = false;
411: remoteSocket = remoteSocket.trim();
412:
413: int typeIndex = remoteSocket.indexOf("://");
414:
415: if (typeIndex > 0) {
416: String type = remoteSocket.substring(0, typeIndex);
417: remoteSocket = remoteSocket.substring(typeIndex + 3);
418:
419: if (type.equals("tcp")) {
420: } else if (type.equals("ssl")) {
421: isSecure = true;
422: } else {
423: env
424: .warning(L
425: .l(
426: "unrecognized socket transport: {0}",
427: type));
428:
429: return BooleanValue.FALSE;
430: }
431: }
432:
433: int colonIndex = remoteSocket.lastIndexOf(':');
434:
435: String host = remoteSocket;
436: int port = 80;
437:
438: if (colonIndex > 0) {
439: host = remoteSocket.substring(0, colonIndex);
440:
441: port = 0;
442:
443: for (int i = colonIndex + 1; i < remoteSocket.length(); i++) {
444: char ch = remoteSocket.charAt(i);
445:
446: if ('0' <= ch && ch <= '9')
447: port = port * 10 + ch - '0';
448: else
449: break;
450: }
451: }
452:
453: Socket socket;
454:
455: if (isSecure)
456: socket = SSLSocketFactory.getDefault().createSocket(
457: host, port);
458: else
459: socket = SocketFactory.getDefault().createSocket(host,
460: port);
461:
462: socket.setSoTimeout((int) (timeout * 1000));
463:
464: SocketInputOutput stream = new SocketInputOutput(env,
465: socket, SocketInputOutput.Domain.AF_INET);
466:
467: stream.init();
468:
469: return env.wrapJava(stream);
470: } catch (UnknownHostException e) {
471: errorStr.set(env.createString(e.getMessage()));
472:
473: return BooleanValue.FALSE;
474: } catch (IOException e) {
475: errorStr.set(env.createString(e.getMessage()));
476:
477: return BooleanValue.FALSE;
478: }
479: }
480:
481: public static void stream_wrapper_register(StringValue protocol,
482: ProtocolWrapper wrapper) {
483: _wrapperMap.put(protocol.toString(), wrapper);
484:
485: _wrapperArray.append(protocol);
486: }
487:
488: /**
489: * Register a wrapper for a protocol.
490: */
491: public static boolean stream_wrapper_register(Env env,
492: StringValue protocol, String className) {
493: if (_wrapperMap.containsKey(protocol.toString()))
494: return false;
495:
496: QuercusClass qClass = env.getClass(className);
497:
498: stream_wrapper_register(protocol, new ProtocolWrapper(qClass));
499:
500: return true;
501: }
502:
503: /**
504: * Register a wrapper for a protocol.
505: */
506: public static boolean stream_wrapper_restore(Env env,
507: StringValue protocol) {
508: if (!_unregisteredWrapperMap.containsKey(protocol.toString()))
509: return false;
510:
511: ProtocolWrapper oldWrapper = _unregisteredWrapperMap
512: .remove(protocol.toString());
513:
514: stream_wrapper_register(protocol, oldWrapper);
515:
516: return true;
517: }
518:
519: /**
520: * Register a wrapper for a protocol.
521: */
522: public static boolean stream_wrapper_unregister(Env env,
523: StringValue protocol) {
524: if (!_wrapperMap.containsKey(protocol.toString()))
525: return false;
526:
527: _unregisteredWrapperMap.put(protocol.toString(), _wrapperMap
528: .remove(protocol.toString()));
529:
530: _wrapperArray.remove(protocol);
531:
532: return true;
533: }
534:
535: protected static ProtocolWrapper getWrapper(String protocol) {
536: return _wrapperMap.get(protocol);
537: }
538:
539: static {
540: _wrapperArray.append(new StringBuilderValue("quercus"));
541: _wrapperArray.append(new StringBuilderValue("file"));
542: _wrapperArray.append(new StringBuilderValue("http"));
543: _wrapperArray.append(new StringBuilderValue("ftp"));
544: }
545:
546: static {
547: _constMap.put("STREAM_URL_STAT_LINK", new LongValue(
548: STREAM_URL_STAT_LINK));
549: _constMap.put("STREAM_URL_STAT_QUIET", new LongValue(
550: STREAM_URL_STAT_QUIET));
551:
552: _constMap.put("STREAM_FILTER_READ", new LongValue(
553: STREAM_FILTER_READ));
554: _constMap.put("STREAM_FILTER_WRITE", new LongValue(
555: STREAM_FILTER_WRITE));
556: _constMap.put("STREAM_FILTER_ALL", new LongValue(
557: STREAM_FILTER_ALL));
558:
559: _constMap.put("PSFS_PASS_ON", new LongValue(PSFS_PASS_ON));
560: _constMap.put("PSFS_FEED_ME", new LongValue(PSFS_FEED_ME));
561: _constMap.put("PSFS_ERR_FATAL", new LongValue(PSFS_ERR_FATAL));
562:
563: _constMap
564: .put("STREAM_USE_PATH", new LongValue(STREAM_USE_PATH));
565: _constMap.put("STREAM_REPORT_ERRORS", new LongValue(
566: STREAM_REPORT_ERRORS));
567:
568: _constMap.put("STREAM_CLIENT_ASYNC_CONNECT", new LongValue(
569: STREAM_CLIENT_ASYNC_CONNECT));
570: _constMap.put("STREAM_CLIENT_CONNECT", new LongValue(
571: STREAM_CLIENT_CONNECT));
572: _constMap.put("STREAM_CLIENT_PERSISTENT", new LongValue(
573: STREAM_CLIENT_PERSISTENT));
574:
575: _constMap.put("STREAM_SERVER_BIND", new LongValue(
576: STREAM_SERVER_BIND));
577: _constMap.put("STREAM_SERVER_LISTEN", new LongValue(
578: STREAM_SERVER_LISTEN));
579: }
580: }
|