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.session;
031:
032: import com.caucho.quercus.annotation.Optional;
033: import com.caucho.quercus.env.*;
034: import com.caucho.quercus.lib.OutputModule;
035: import com.caucho.quercus.module.AbstractQuercusModule;
036: import com.caucho.quercus.module.ModuleStartupListener;
037: import com.caucho.quercus.module.IniDefinitions;
038: import com.caucho.quercus.module.IniDefinition;
039: import com.caucho.util.L10N;
040:
041: import javax.servlet.http.Cookie;
042: import javax.servlet.http.HttpServletResponse;
043: import java.util.logging.Logger;
044: import java.util.Iterator;
045:
046: /**
047: * Quercus session handling
048: */
049: public class SessionModule extends AbstractQuercusModule implements
050: ModuleStartupListener {
051: private static final L10N L = new L10N(SessionModule.class);
052: private static final Logger log = Logger
053: .getLogger(SessionModule.class.getName());
054:
055: private static final IniDefinitions _iniDefinitions = new IniDefinitions();
056:
057: /**
058: * Returns the default php.ini values.
059: */
060: public IniDefinitions getIniDefinitions() {
061: return _iniDefinitions;
062: }
063:
064: public String[] getLoadedExtensions() {
065: return new String[] { "session" };
066: }
067:
068: public void startup(Env env) {
069: if (env.getConfigVar("session.auto_start").toBoolean())
070: session_start(env);
071: }
072:
073: /**
074: * Returns and/or sets the value of session.cache_limiter, affecting the
075: * cache related headers that are sent as a result of a call to
076: * {@link #session_start(Env)}.
077: *
078: * If the optional parameter is not supplied, this function simply returns the existing value.
079: * If the optional parameter is supplied, the returned value
080: * is the old value that was set before the new value is applied.
081: *
082: * Valid values are "nocache" (the default), "private", "private_no_expire",
083: * and "public". If a value other than these values is supplied, then a warning is produced
084: * and no cache related headers will be sent to the client.
085: */
086: public Value session_cache_limiter(Env env, @Optional
087: String newValue) {
088: Value value = env.getIni("session.cache_limiter");
089:
090: if (newValue == null || "".equals(newValue)) // XXX: php/1k16
091: return value;
092:
093: env.setIni("session.cache_limiter", newValue);
094:
095: return value;
096: }
097:
098: public Value session_cache_expire(Env env, @Optional
099: Value newValue) {
100: Value value = (LongValue) env.getSpecialValue("cache_expire");
101:
102: if (value == null)
103: value = env.getIni("session.cache_expire");
104:
105: if (newValue != null && !newValue.isDefault())
106: env.setSpecialValue("cache_expire", newValue);
107:
108: return LongValue.create(value.toLong());
109: }
110:
111: /**
112: * Alias of session_write_close.
113: */
114: public static Value session_commit(Env env) {
115: return session_write_close(env);
116: }
117:
118: /**
119: * Encodes the session values.
120: */
121: public static boolean session_decode(Env env, StringValue value) {
122: Value session = env.getGlobalValue("_SESSION");
123:
124: if (!(session instanceof SessionArrayValue)) {
125: env.warning(L.l("session_decode requires valid session"));
126: return false;
127: }
128:
129: return ((SessionArrayValue) session).decode(env, value
130: .toString());
131: }
132:
133: /**
134: * Encodes the session values.
135: */
136: public static String session_encode(Env env) {
137: Value session = env.getGlobalValue("_SESSION");
138:
139: if (!(session instanceof SessionArrayValue)) {
140: env.warning(L.l("session_encode requires valid session"));
141: return null;
142: }
143:
144: return ((SessionArrayValue) session).encode();
145: }
146:
147: /**
148: * Destroys the session
149: */
150: public static boolean session_destroy(Env env) {
151: SessionArrayValue session = env.getSession();
152:
153: if (session == null)
154: return false;
155:
156: env.destroySession(session.getId());
157:
158: return true;
159: }
160:
161: /**
162: * Returns the session cookie parameters
163: */
164: public static ArrayValue session_get_cookie_params(Env env) {
165: ArrayValue array = new ArrayValueImpl();
166:
167: array.put(env, "lifetime", env
168: .getIniLong("session.cookie_lifetime"));
169: array.put(env, "path", env.getIniString("session.cookie_path"));
170: array.put(env, "domain", env
171: .getIniString("session.cookie_domain"));
172: array.put(env, "secure", env
173: .getIniBoolean("session.cookie_secure"));
174:
175: return array;
176: }
177:
178: /**
179: * Returns the session id
180: */
181: public static String session_id(Env env, @Optional
182: String id) {
183: Value sessionIdValue = (Value) env
184: .getSpecialValue("caucho.session_id");
185:
186: String oldValue;
187:
188: if (sessionIdValue != null)
189: oldValue = sessionIdValue.toString();
190: else
191: oldValue = "";
192:
193: if (id != null && !"".equals(id))
194: env.setSpecialValue("caucho.session_id", env
195: .createString(id));
196:
197: return oldValue;
198: }
199:
200: /**
201: * Returns true if a session variable is registered.
202: */
203: public static boolean session_is_registered(Env env, String name) {
204: return env.getGlobalValue("_SESSION").get(
205: env.createString(name)).isset();
206: }
207:
208: /**
209: * Returns the object's class name
210: */
211: public Value session_module_name(Env env, @Optional
212: String newValue) {
213: Value value = env.getIni("session.save_handler");
214:
215: if (newValue != null && !newValue.equals(""))
216: env.setIni("session.save_handler", newValue);
217:
218: return value;
219: }
220:
221: /**
222: * Returns the object's class name
223: */
224: public Value session_name(Env env, @Optional
225: String newValue) {
226: Value value = env.getIni("session.name");
227:
228: if (newValue != null && !newValue.equals(""))
229: env.setIni("session.name", newValue);
230:
231: return value;
232: }
233:
234: /**
235: * Regenerates the session id.
236: *
237: * This function first creates a new session id. The old session values are
238: * migrated to this new session. Then a new session cookie is sent (XXX: send
239: * only if URL rewriting is off?). Changing the session ID should be transparent.
240: * Therefore, session callbacks should not be called.
241: */
242: public static boolean session_regenerate_id(Env env, @Optional
243: boolean deleteOld) {
244: String sessionId = env.generateSessionId();
245:
246: if (deleteOld) {
247: session_destroy(env);
248:
249: SessionArrayValue session = env.createSession(sessionId,
250: true);
251: env.setSession(session);
252: } else {
253: SessionArrayValue session = env.getSession();
254: session.setId(sessionId);
255: }
256:
257: // update environment to new session id
258: session_id(env, sessionId);
259:
260: if (env.getIni("session.use_cookies").toBoolean())
261: generateSessionCookie(env, sessionId);
262:
263: return true;
264: }
265:
266: /**
267: * Registers global variables in the session.
268: */
269: public boolean session_register(Env env, Value[] values) {
270: Value session = env.getGlobalValue("_SESSION");
271:
272: if (!session.isArray()) {
273: session_start(env);
274: session = env.getGlobalValue("_SESSION");
275: }
276:
277: for (int i = 0; i < values.length; i++)
278: sessionRegisterImpl(env, (ArrayValue) session, values[i]);
279:
280: return true;
281: }
282:
283: /**
284: * Registers global variables in the session.
285: */
286: private void sessionRegisterImpl(Env env, ArrayValue session,
287: Value nameV) {
288: nameV = nameV.toValue();
289:
290: if (nameV instanceof StringValue) {
291: String name = nameV.toString();
292:
293: Value var = env.getGlobalVar(name);
294:
295: Value value = session.get(nameV);
296:
297: if (value.isset())
298: var.set(value);
299:
300: session.put(nameV, var);
301: } else if (nameV.isArray()) {
302: ArrayValue array = (ArrayValue) nameV.toValue();
303:
304: for (Value subValue : array.values()) {
305: sessionRegisterImpl(env, session, subValue);
306: }
307: }
308: }
309:
310: /**
311: * Returns the session's save path
312: */
313: public Value session_save_path(Env env, @Optional
314: String newValue) {
315: Value value = env.getIni("session.save_path");
316:
317: if (newValue != null && !newValue.equals(""))
318: env.setIni("session.save_path", newValue);
319:
320: if (value.isNull() || value.length() == 0) {
321: // XXX: should we create work directory if does not exist?
322: value = env.createString(env.getWorkDir().getPath());
323: }
324:
325: return value;
326: }
327:
328: /**
329: * Sets the session cookie parameters
330: */
331: public Value session_set_cookie_params(Env env, long lifetime,
332: @Optional
333: Value path, @Optional
334: Value domain, @Optional
335: Value secure) {
336: env.setIni("session.cookie_lifetime", String.valueOf(lifetime));
337:
338: if (path.isset())
339: env.setIni("session.cookie_path", path.toString());
340:
341: if (domain.isset())
342: env.setIni("session.cookie_domain", domain.toString());
343:
344: if (secure.isset())
345: env.setIni("session.cookie_secure",
346: secure.toBoolean() ? "1" : "0");
347:
348: return NullValue.NULL;
349: }
350:
351: /**
352: * Sets the session save handler
353: */
354: public boolean session_set_save_handler(Env env, Callback open,
355: Callback close, Callback read, Callback write,
356: Callback directory, Callback gc)
357:
358: {
359: SessionCallback cb = new SessionCallback(open, close, read,
360: write, directory, gc);
361:
362: env.setSessionCallback(cb);
363:
364: return true;
365: }
366:
367: /**
368: * Start the session
369: */
370: public static boolean session_start(Env env) {
371: if (env.getSession() != null) {
372: env.notice(L.l("session has already been started"));
373: return true;
374: }
375:
376: SessionCallback callback = env.getSessionCallback();
377:
378: Value sessionIdValue = (Value) env
379: .getSpecialValue("caucho.session_id");
380: String sessionId = null;
381:
382: HttpServletResponse response = env.getResponse();
383:
384: env.removeConstant("SID");
385:
386: String cookieName = env.getIni("session.name").toString();
387: boolean generateCookie = true;
388: boolean create = false;
389:
390: if (callback != null) {
391: String savePath = env.getIni("session.save_path")
392: .toString();
393:
394: if (savePath == null || "".equals(savePath))
395: callback.open(env, env.getWorkDir().getPath(),
396: cookieName);
397: else
398: callback.open(env, savePath, cookieName);
399: }
400:
401: //
402: // Use cookies to transmit session id
403: //
404: if (env.getIni("session.use_cookies").toBoolean()) {
405: if (sessionIdValue != null)
406: sessionId = sessionIdValue.toString();
407:
408: if (sessionId == null || "".equals(sessionId)) {
409: Cookie[] cookies = env.getRequest().getCookies();
410:
411: for (int i = 0; cookies != null && i < cookies.length; i++) {
412: if (cookies[i].getName().equals(cookieName)
413: && !"".equals(cookies[i].getValue())) {
414: sessionId = cookies[i].getValue();
415: generateCookie = false;
416: }
417: }
418: }
419:
420: if (!generateCookie)
421: env.addConstant("SID", env.createEmptyString(), false);
422: }
423:
424: //
425: // Use URL rewriting to transmit session id
426: //
427:
428: if (env.getIniBoolean("session.use_trans_sid")
429: && !env.getIniBoolean("session.use_only_cookies")) {
430: if (sessionId == null) {
431: if (sessionIdValue != null)
432: sessionId = sessionIdValue.toString();
433:
434: if (sessionId == null || "".equals(sessionId))
435: sessionId = env.getRequest().getParameter(
436: cookieName);
437:
438: if (sessionId == null || "".equals(sessionId)) {
439: sessionId = env.generateSessionId();
440: create = true;
441: }
442: }
443:
444: env.addConstant("SID", env.createString(cookieName + '='
445: + sessionId), false);
446:
447: OutputModule.pushUrlRewriter(env);
448: }
449:
450: if (sessionId == null || "".equals(sessionId)) {
451: sessionId = env.generateSessionId();
452: create = true;
453: }
454:
455: if (response.isCommitted())
456: env
457: .warning(L
458: .l("cannot send session cache limiter headers because response is committed"));
459: else {
460: Value cacheLimiterValue = env
461: .getIni("session.cache_limiter");
462: String cacheLimiter = String.valueOf(cacheLimiterValue);
463:
464: Value cacheExpireValue = (LongValue) env
465: .getSpecialValue("cache_expire");
466:
467: if (cacheExpireValue == null)
468: cacheExpireValue = env.getIni("session.cache_expire");
469:
470: int cacheExpire = cacheExpireValue.toInt() * 60;
471:
472: if ("nocache".equals(cacheLimiter)) {
473: response.setHeader("Expires",
474: "Thu, 19 Nov 1981 08:52:00 GMT");
475: response
476: .setHeader("Cache-Control",
477: "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
478: response.setHeader("Pragma", "no-cache");
479: } else if ("private".equals(cacheLimiter)) {
480: response.setHeader("Cache-Control", "private, max-age="
481: + cacheExpire + ", pre-check=" + cacheExpire);
482: } else if ("private_no_expire".equals(cacheLimiter)) {
483: response.setHeader("Cache-Control", "private, max-age="
484: + cacheExpire + ", pre-check=" + cacheExpire);
485: } else if ("public".equals(cacheLimiter)) {
486: response.setHeader("Cache-Control", "max-age="
487: + cacheExpire + ", pre-check=" + cacheExpire);
488: } else if ("none".equals(cacheLimiter)) {
489: } else {
490: response.setHeader("Cache-Control", cacheLimiter
491: + ", max-age=" + cacheExpire + ", pre-check="
492: + cacheExpire);
493: }
494: }
495:
496: SessionArrayValue session = env
497: .createSession(sessionId, create);
498: sessionId = session.getId();
499:
500: if (env.getIni("session.use_cookies").toBoolean()
501: && generateCookie) {
502: generateSessionCookie(env, sessionId);
503: }
504: env.setSpecialValue("caucho.session_id", env
505: .createString(sessionId));
506:
507: return true;
508: }
509:
510: /**
511: * Sends a new session cookie.
512: */
513: private static void generateSessionCookie(Env env, String sessionId) {
514: final HttpServletResponse response = env.getResponse();
515:
516: String cookieName = env.getIni("session.name").toString();
517:
518: env.addConstant("SID", env.createString(cookieName + '='
519: + sessionId), false);
520:
521: Cookie cookie = new Cookie(cookieName, sessionId);
522: cookie.setVersion(1);
523:
524: if (response.isCommitted()) {
525: env
526: .warning(L
527: .l("cannot send session cookie because response is committed"));
528: } else {
529: Value path = env.getIni("session.cookie_path");
530: cookie.setPath(path.toString());
531:
532: Value maxAge = env.getIni("session.cookie_lifetime");
533:
534: if (maxAge.toInt() != 0)
535: cookie.setMaxAge(maxAge.toInt());
536:
537: Value domain = env.getIni("session.cookie_domain");
538:
539: // this is for 3rd party servlet containers that don't check the domain
540: // before sending the cookie
541: if (domain.length() > 0) {
542: cookie.setDomain(domain.toString());
543: }
544:
545: Value secure = env.getIni("session.cookie_secure");
546: cookie.setSecure(secure.toBoolean());
547:
548: response.addCookie(cookie);
549: }
550: }
551:
552: /**
553: * Unsets the specified session values
554: */
555: public boolean session_unregister(Env env, Value key) {
556: Value value = env.getGlobalValue("_SESSION");
557:
558: if (!value.isArray())
559: return false;
560:
561: value.remove(key);
562:
563: return true;
564: }
565:
566: /**
567: * Unsets the session values
568: */
569: public Value session_unset(Env env) {
570: Value value = env.getGlobalValue("_SESSION");
571:
572: if (!value.isArray())
573: return NullValue.NULL;
574:
575: ArrayValue array = value.toArrayValue(env);
576:
577: array.clear();
578:
579: return NullValue.NULL;
580: }
581:
582: /**
583: * Writes the session and closes it.
584: */
585: public static Value session_write_close(Env env) {
586: env.sessionWriteClose();
587:
588: return NullValue.NULL;
589: }
590:
591: /**
592: * Converts an integer to a printable character
593: */
594: private static char encode(long code) {
595: code = code & 0x3f;
596:
597: if (code < 26)
598: return (char) ('a' + code);
599: else if (code < 52)
600: return (char) ('A' + code - 26);
601: else if (code < 62)
602: return (char) ('0' + code - 52);
603: else if (code == 62)
604: return '_';
605: else
606: return '-';
607: }
608:
609: static final IniDefinition INI_SESSION_SAVE_PATH = _iniDefinitions
610: .add("session.save_path", "", PHP_INI_ALL);
611: static final IniDefinition INI_SESSION_NAME = _iniDefinitions.add(
612: "session.name", "PHPSESSID", PHP_INI_ALL);
613: static final IniDefinition INI_SESSION_SAVE_HANDLER = _iniDefinitions
614: .add("session.save_handler", "files", PHP_INI_ALL);
615: static final IniDefinition INI_SESSION_AUTO_START = _iniDefinitions
616: .add("session.auto_start", false, PHP_INI_ALL);
617: static final IniDefinition INI_SESSION_GC_PROBABILITY_START = _iniDefinitions
618: .add("session.gc_probability_start", true, PHP_INI_ALL);
619: static final IniDefinition INI_SESSION_GC_DIVISOR = _iniDefinitions
620: .add("session.gc_divisor", 100, PHP_INI_ALL);
621: static final IniDefinition INI_SESSION_GC_MAXLIFETIME = _iniDefinitions
622: .add("session.gc_maxlifetime", 1440, PHP_INI_ALL);
623: static final IniDefinition INI_SESSION_SERIALIZE_HANDLER = _iniDefinitions
624: .add("session.serialize_handler", "quercus", PHP_INI_ALL);
625: static final IniDefinition INI_SESSION_COOKIE_LIFETIME = _iniDefinitions
626: .add("session.cookie_lifetime", 0, PHP_INI_ALL);
627: static final IniDefinition INI_SESSION_COOKIE_PATH = _iniDefinitions
628: .add("session.cookie_path", "/", PHP_INI_ALL);
629: static final IniDefinition INI_SESSION_COOKIE_DOMAIN = _iniDefinitions
630: .add("session.cookie_domain", "", PHP_INI_ALL);
631: static final IniDefinition INI_SESSION_COOKIE_SECURE = _iniDefinitions
632: .add("session.cookie_secure", "", PHP_INI_ALL);
633: static final IniDefinition INI_SESSION_USE_COOKIES = _iniDefinitions
634: .add("session.use_cookies", true, PHP_INI_ALL);
635: static final IniDefinition INI_SESSION_USE_ONLY_COOKIES = _iniDefinitions
636: .add("session.use_only_cookies", true, PHP_INI_ALL);
637: static final IniDefinition INI_SESSION_REFERER_CHECK = _iniDefinitions
638: .add("session.referer_check", "", PHP_INI_ALL);
639: static final IniDefinition INI_SESSION_ENTROPY_FILE = _iniDefinitions
640: .add("session.entropy_file", "", PHP_INI_ALL);
641: static final IniDefinition INI_SESSION_ENTROPY_LENGTH = _iniDefinitions
642: .add("session.entropy_length", false, PHP_INI_ALL);
643: static final IniDefinition INI_SESSION_CACHE_LIMITER = _iniDefinitions
644: .add("session.cache_limiter", "nocache", PHP_INI_ALL);
645: static final IniDefinition INI_SESSION_CACHE_EXPIRE = _iniDefinitions
646: .add("session.cache_expire", 180, PHP_INI_ALL);
647: static final IniDefinition INI_SESSION_USE_TRANS_SID = _iniDefinitions
648: .add("session.use_trans_sid", false, PHP_INI_ALL);
649: static final IniDefinition INI_SESSION_BUG_COMPAT_42 = _iniDefinitions
650: .add("session.bug_compat_42", true, PHP_INI_ALL);
651: static final IniDefinition INI_SESSION_BUG_COMPAT_WARN = _iniDefinitions
652: .add("session.bug_compat_warn", true, PHP_INI_ALL);
653: static final IniDefinition INI_SESSION_HASH_FUNCTION = _iniDefinitions
654: .add("session.hash_function", false, PHP_INI_ALL);
655: static final IniDefinition INI_SESSION_HASH_BITS_PER_CHARACTER = _iniDefinitions
656: .add("session.hash_bits_per_character", 4, PHP_INI_ALL);
657: static final IniDefinition INI_URL_REWRITER_TAGS = _iniDefinitions
658: .add("url_rewriter.tags",
659: "a=href,area=href,frame=src,form=,fieldset=",
660: PHP_INI_ALL);
661: }
|