See More

/* * (C) Gregory Trubetskoy Nov 1998 * * mod_python.c * * $Id: mod_python.c,v 1.1 2000/05/04 23:08:24 grisha Exp $ * * See accompanying documentation and source code comments * for details. See COPYRIGHT file for Copyright. * * Apr 2000 - rename to mod_python and go apache-specific. * Nov 1998 - support for multiple interpreters introduced. * May 1998 - initial release. * */ /* Apache headers */ #include "httpd.h" #include "http_config.h" #include "http_protocol.h" #include "util_script.h" #include "http_log.h" /* Python headers */ #include "Python.h" #include "structmember.h" /****************************************************************** Declarations ******************************************************************/ #define VERSION_COMPONENT "mod_python/2.0a" #define MODULENAME "apache" #define INITSTRING "apache.init()" #define INTERP_ATTR "__interpreter__" /* debugging? Use ./httpd -X when on */ static int debug = 1; /* Are we in single interpreter mode? */ static int single_mode = 0; /* List of available Python obCallBacks/Interpreters * (In a Python dictionary) */ static PyObject * interpreters = NULL; /* The CallBack object. This variable is used as * a way to pass a pointer between SetCallBack() * function and make_obcallback(). Nothing else. * Don't rely on its value. */ static PyObject *obCallBack = NULL; /* This function is used with ap_register_cleanup() */ void noop(void *data) {}; void python_decref(void *object); /* some forward declarations */ PyObject * make_obcallback(const char *module, const char *initstring); PyObject * tuple_from_array_header(const array_header *ah); PyObject * get_obcallback(const char *name, server_rec * req); /********************************* Python things *********************************/ /********************************* members of _apache module *********************************/ /* froward declarations */ static PyObject * SetCallBack(PyObject *self, PyObject *args); static PyObject * log_error(PyObject *self, PyObject *args); static PyObject * make_table(PyObject *self, PyObject *args); /* methods of _apache */ static struct PyMethodDef _apache_module_methods[] = { {"SetCallBack", (PyCFunction)SetCallBack, METH_VARARGS}, {"log_error", (PyCFunction)log_error, METH_VARARGS}, {"make_table", (PyCFunction)make_table, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; /******************************** tableobject ********************************/ typedef struct tableobject { PyObject_VAR_HEAD table *table; pool *pool; } tableobject; static PyTypeObject tableobjecttype; #define is_tableobject(op) ((op)->ob_type == &tableobjecttype) static PyObject * tablegetitem (tableobject *self, PyObject *key ); static PyObject * table_has_key (tableobject *self, PyObject *args ); static PyObject * table_keys (tableobject *self); static int tablelength (tableobject *self); static int tablesetitem (tableobject *self, PyObject *key, PyObject *val); static int tb_setitem (tableobject *self, const char *key, const char *val); static tableobject * make_tableobject(table * t); static PyMethodDef tablemethods[] = { { "keys", (PyCFunction)table_keys, 1}, { "has_key", (PyCFunction)table_has_key, 1}, { NULL, NULL } /* sentinel */ }; static PyMappingMethods table_mapping = { (inquiry) tablelength, /*mp_length*/ (binaryfunc) tablegetitem, /*mp_subscript*/ (objobjargproc) tablesetitem, /*mp_ass_subscript*/ }; /* another forward */ tableobject * headers_in(request_rec *req); /******************************** serverobject ********************************/ typedef struct serverobject { PyObject_HEAD server_rec *server; PyObject *next; } serverobject; static PyTypeObject serverobjecttype; #define is_serverobject(op) ((op)->ob_type == &serverobjecttype) #define OFF(x) offsetof(server_rec, x) static struct memberlist server_memberlist[] = { {"defn_name", T_STRING, OFF(defn_name), RO}, {"defn_line_number", T_INT, OFF(defn_line_number), RO}, {"srm_confname", T_STRING, OFF(srm_confname), RO}, {"access_confname", T_STRING, OFF(access_confname), RO}, {"server_admin", T_STRING, OFF(server_admin), RO}, {"server_hostname", T_STRING, OFF(server_hostname), RO}, {"port", T_SHORT, OFF(port), RO}, {"error_fname", T_STRING, OFF(error_fname), RO}, {"loglevel", T_INT, OFF(loglevel), RO}, {"is_virtual", T_INT, OFF(is_virtual), RO}, /* XXX implement module_config ? */ /* XXX implement lookup_defaults ? */ /* XXX implement server_addr_rec ? */ {"timeout", T_INT, OFF(timeout), RO}, {"keep_alive_timeout", T_INT, OFF(keep_alive_timeout), RO}, {"keep_alive_max", T_INT, OFF(keep_alive_max), RO}, {"keep_alive", T_INT, OFF(keep_alive), RO}, {"send_buffer_size", T_INT, OFF(send_buffer_size), RO}, {"path", T_STRING, OFF(path), RO}, {"pathlen", T_INT, OFF(pathlen), RO}, {"server_uid", T_INT, OFF(server_uid), RO}, {"server_gid", T_INT, OFF(server_gid), RO}, {NULL} /* Sentinel */ }; /******************************** connobject ********************************/ typedef struct connobject { PyObject_HEAD conn_rec *conn; PyObject *server; PyObject *base_server; } connobject; static PyTypeObject connobjecttype; #define is_connobject(op) ((op)->ob_type == &connobjecttype) #undef OFF #define OFF(x) offsetof(conn_rec, x) static struct memberlist conn_memberlist[] = { /* server in getattr */ /* base_server in getattr */ /* XXX vhost_lookup_data? */ {"child_num", T_INT, OFF(child_num), RO}, /* XXX BUFF? */ /* XXX struct sockaddr_in local_addr? */ /* XXX struct sockaddr_in remote_addr? */ {"remote_ip", T_STRING, OFF(remote_ip), RO}, {"remote_host", T_STRING, OFF(remote_host), RO}, {"remote_logname", T_STRING, OFF(remote_logname), RO}, {"user", T_STRING, OFF(user), RO}, {"ap_auth_type", T_STRING, OFF(ap_auth_type), RO}, /* XXX aborted, keepalive, keptalive, double_reverse, keepalives ? */ {NULL} /* Sentinel */ }; /******************************** requestobject ********************************/ typedef struct requestobject { PyObject_HEAD request_rec * request_rec; PyObject * connection; PyObject * server; PyObject * next; PyObject * prev; PyObject * main; tableobject * headers_in; tableobject * headers_out; tableobject * err_headers_out; tableobject * subprocess_env; tableobject * notes; int header_sent; } requestobject; static PyTypeObject requestobjecttype; #define is_requestobject(op) ((op)->ob_type == &requestobjecttype) static PyObject * req_send_http_header (requestobject *self, PyObject *args); static PyObject * req_get_basic_auth_pw (requestobject *self, PyObject *args); static PyObject * req_write (requestobject *self, PyObject *args); static PyObject * req_read (requestobject *self, PyObject *args); static PyObject * req_get_config (requestobject *self, PyObject *args); static PyObject * req_get_options (requestobject *self, PyObject *args); static PyObject * req_get_dirs (requestobject *self, PyObject *args); static PyObject * req_add_common_vars (requestobject *self, PyObject *args); static PyMethodDef requestobjectmethods[] = { {"send_http_header", (PyCFunction) req_send_http_header, METH_VARARGS}, {"get_basic_auth_pw", (PyCFunction) req_get_basic_auth_pw, METH_VARARGS}, {"write", (PyCFunction) req_write, METH_VARARGS}, {"read", (PyCFunction) req_read, METH_VARARGS}, {"get_config", (PyCFunction) req_get_config, METH_VARARGS}, {"get_options", (PyCFunction) req_get_options, METH_VARARGS}, {"get_dirs", (PyCFunction) req_get_dirs, METH_VARARGS}, {"add_common_vars", (PyCFunction) req_add_common_vars, METH_VARARGS}, { NULL, NULL } /* sentinel */ }; #undef OFF #define OFF(x) offsetof(request_rec, x) static struct memberlist request_memberlist[] = { /* connection, server, next, prev, main in getattr */ {"the_request", T_STRING, OFF(the_request), RO}, {"assbackwards", T_INT, OFF(assbackwards), RO}, {"proxyreq", T_INT, OFF(proxyreq), RO}, {"header_only", T_INT, OFF(header_only), RO}, {"protocol", T_STRING, OFF(protocol), RO}, {"proto_num", T_INT, OFF(proto_num), RO}, {"hostname", T_STRING, OFF(hostname), RO}, {"request_time", T_LONG, OFF(request_time), RO}, {"status_line", T_STRING, OFF(status_line), RO}, {"status", T_INT, OFF(status) }, {"method", T_STRING, OFF(method), RO}, {"method_number", T_INT, OFF(method_number), RO}, {"allowed", T_INT, OFF(allowed), RO}, {"sent_bodyct", T_INT, OFF(sent_bodyct), RO}, {"bytes_sent", T_LONG, OFF(bytes_sent), RO}, {"mtime", T_LONG, OFF(mtime), RO}, {"chunked", T_INT, OFF(chunked), RO}, {"byterange", T_INT, OFF(byterange), RO}, {"boundary", T_STRING, OFF(boundary), RO}, {"range", T_STRING, OFF(range), RO}, {"clength", T_LONG, OFF(clength), RO}, {"remaining", T_LONG, OFF(remaining), RO}, {"read_length", T_LONG, OFF(read_length), RO}, {"read_body", T_INT, OFF(read_body), RO}, {"read_chunked", T_INT, OFF(read_chunked), RO}, {"content_type", T_STRING, OFF(content_type) }, {"handler", T_STRING, OFF(handler), RO}, {"content_encoding", T_STRING, OFF(content_encoding), RO}, {"content_language", T_STRING, OFF(content_language), RO}, /* XXX content_languages */ {"no_cache", T_INT, OFF(no_cache), RO}, {"no_local_copy", T_INT, OFF(no_local_copy), RO}, {"unparsed_uri", T_STRING, OFF(unparsed_uri), RO}, {"uri", T_STRING, OFF(uri), RO}, {"filename", T_STRING, OFF(filename), RO}, {"path_info", T_STRING, OFF(path_info), RO}, {"args", T_STRING, OFF(args), RO}, /* XXX finfo */ /* XXX parsed_uri */ /* XXX per_dir_config */ /* XXX request_config */ /* XXX htaccess */ {NULL} /* Sentinel */ }; /******************************** *** end of Python things *** ********************************/ /******************************** Apache things ********************************/ /* Apache module declaration */ module MODULE_VAR_EXPORT python_module; extern module python_module; /* structure describing per directory configuration parameters */ typedef struct { int authoritative; char *config_dir; table *options; table *directives; table *dirs; } py_dir_config; /******************************** *** end of Apache things *** ********************************/ /****************************************************************** *** end of declarations *** ******************************************************************/ /****************************************************************** Python objects and their methods ******************************************************************/ /******************************** table object ********************************/ /* * This is a mapping of a Python object to an Apache table. * * This object behaves like a dictionary. Note that the * underlying table can have duplicate keys, which can never * happen to a Python dictionary. But this is such a rare thing * that I can't even think of a possible scenario or implications. * */ /** ** make_tableobject ** * This routine creates a Python tableobject given an Apache * table pointer. * */ static tableobject * make_tableobject(table * t) { tableobject *result; result = PyMem_NEW(tableobject, 1); if (! result) return (tableobject *) PyErr_NoMemory(); result->table = t; result->ob_type = &tableobjecttype; result->pool = NULL; _Py_NewReference(result); return result; } /** ** make_table ** * This returns a new object of built-in type table. * * NOTE: The ap_table gets greated in its own pool, which lives * throught the live of the tableobject. This is because this * object may persist from hit to hit. * * make_table() * */ static PyObject * make_table(PyObject *self, PyObject *args) { tableobject *t; pool *p; p = ap_make_sub_pool(NULL); /* two is a wild guess */ t = make_tableobject(ap_make_table(p, 2)); /* remember the pointer to our own pool */ t->pool = p; return (PyObject *)t; } /** ** table_getattr ** * Gets table's attributes */ static PyObject * table_getattr(PyObject *self, char *name) { return Py_FindMethod(tablemethods, self, name); } /** ** tablegetitem ** * Gets a dictionary item */ static PyObject * tablegetitem(tableobject *self, PyObject *key) { const char *v; char *k; k = PyString_AsString(key); v = ap_table_get(self->table, k); if (! v) { PyErr_SetObject(PyExc_KeyError, key); return NULL; } return PyString_FromString(v); } /** ** table_has_key ** */ static PyObject * table_has_key(tableobject *self, PyObject *args) { const char *val, *key; if (! PyArg_ParseTuple(args, "s", &key)) return NULL; val = ap_table_get (self->table, key); if (val) return PyInt_FromLong(1); else return PyInt_FromLong(0); } /** ** tablelength ** * Number of elements in a table. Called * when you do len(table) in Python. */ static int tablelength(tableobject *self) { return ap_table_elts(self->table)->nelts; }; /** ** tablesetitem ** * insert into table dictionary-style * *** NOTE *** * Since the underlying ap_table_set makes a *copy* of the string, * there is no need to increment the reference to the Python * string passed in. */ static int tablesetitem(tableobject *self, PyObject *key, PyObject *val) { char *k; if (key && !PyString_Check(key)) { PyErr_SetString(PyExc_TypeError, "table keys must be strings"); return -1; } k = PyString_AsString(key); if ((val == Py_None) || (val == NULL)) { ap_table_unset(self->table, k); } else { if (val && !PyString_Check(val)) { PyErr_SetString(PyExc_TypeError, "table values must be strings"); return -1; } ap_table_set(self->table, k, PyString_AsString(val)); } return 0; }; /** ** tb_setitem ** * This is a wrapper around tablesetitem that takes * char * for convenience, for internal use. */ static int tb_setitem(tableobject *self, const char *key, const char *val) { PyObject *ps1, *ps2; ps1 = PyString_FromString(key); ps2 = PyString_FromString(val); tablesetitem(self, ps1, ps2); Py_DECREF(ps1); Py_DECREF(ps2); return 0; /* prevent complier warning about function never used */ tb_setitem(self, key, val); } /** ** table_dealloc ** * Frees table's memory */ static void table_dealloc(tableobject *self) { if (self->pool) ap_destroy_pool(self->pool); free(self); } /** ** table_repr ** * prints table like a dictionary */ static PyObject * table_repr(tableobject *self) { PyObject *s; array_header *ah; table_entry *elts; int i; s = PyString_FromString("{"); ah = ap_table_elts (self->table); elts = (table_entry *) ah->elts; i = ah->nelts; if (i == 0) PyString_ConcatAndDel(&s, PyString_FromString("}")); while (i--) if (elts[i].key) { PyString_ConcatAndDel(&s, PyString_FromString("'")); PyString_ConcatAndDel(&s, PyString_FromString(elts[i].key)); PyString_ConcatAndDel(&s, PyString_FromString("': '")); PyString_ConcatAndDel(&s, PyString_FromString(elts[i].val)); PyString_ConcatAndDel(&s, PyString_FromString("'")); if (i > 0) PyString_ConcatAndDel(&s, PyString_FromString(", ")); else PyString_ConcatAndDel(&s, PyString_FromString("}")); } return s; } /** ** table_keys ** * * Implements dictionary's keys() method. */ static PyObject * table_keys(tableobject *self) { PyObject *v; array_header *ah; table_entry *elts; int i, j; ah = ap_table_elts(self->table); elts = (table_entry *) ah->elts; v = PyList_New(ah->nelts); for (i = 0, j = 0; i < ah->nelts; i++) { if (elts[i].key) { PyObject *key = PyString_FromString(elts[i].key); PyList_SetItem(v, j, key); j++; } } return v; } /** ** copy_table ** * Merge two tables into one. Matching ley values * in second overlay the first. */ static void copy_table(table *t1, table *t2) { array_header *ah; table_entry *elts; int i; ah = ap_table_elts(t2); elts = (table_entry *)ah->elts; i = ah->nelts; while (i--) if (elts[i].key) ap_table_set(t1, elts[i].key, elts[i].val); } /** ** print_table ** * Print apache table. Only used for debugging. */ static void print_table(table * t) { array_header *ah; table_entry *elts; int i; ah = ap_table_elts (t); elts = (table_entry *) ah->elts; i = ah->nelts; while (i--) if (elts[i].key) printf(" %s: \t%s\n", elts[i].key, elts[i].val); } /** ** print_array ** * Print apache array (of strings). Only used for debugging. */ static void *print_array(array_header *ah) { int i; char **elts; elts = (char **)ah->elts; for (i = 0; i < ah->nelts; ++i) { printf("%s ", elts[i]); } printf("\n"); return NULL; /* avoid compiler warning */ print_array(ah); } /******************************** *** end of table object *** ********************************/ /******************************** server object ********************************/ /* * This is a mapping of a Python object to an Apache server_rec. * */ /** ** make_serverobject ** * This routine creates a Python serverobject given an Apache * server_rec pointer. * */ static serverobject * make_serverobject(server_rec *t) { serverobject *result; result = PyMem_NEW(serverobject, 1); if (! result) return (serverobject *) PyErr_NoMemory(); result->server = t; result->ob_type = &serverobjecttype; result->next = NULL; _Py_NewReference(result); return result; } /** ** server_dealloc ** * */ static void server_dealloc(serverobject *self) { Py_XDECREF(self->next); free(self); } /** ** server_getattr ** * Get server object attributes * * */ static PyObject * server_getattr(serverobject *self, char *name) { if (strcmp(name, "next") == 0) /* server.next serverobject is created as needed */ if (self->next == NULL) { if (self->server->next == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->next = (PyObject *)make_serverobject(self->server->next); Py_INCREF(self->next); return self->next; } } else { Py_INCREF(self->next); return self->next; } else if (strcmp(name, "error_log") == 0) return PyInt_FromLong((long)fileno(self->server->error_log)); else if (strcmp(name, "names") == 0) { return tuple_from_array_header(self->server->names); } else if (strcmp(name, "wild_names") == 0) { return tuple_from_array_header(self->server->wild_names); } else return PyMember_Get((char *)self->server, server_memberlist, name); } /******************************** *** end of server object *** ********************************/ /******************************** conn object ********************************/ /* * This is a mapping of a Python object to an Apache conn_rec. * */ /** ** make_connobject ** * This routine creates a Python connobject given an Apache * conn_rec pointer. * */ static connobject * make_connobject(conn_rec *t) { connobject *result; result = PyMem_NEW(connobject, 1); if (! result) return (connobject *) PyErr_NoMemory(); result->conn = t; result->ob_type = &connobjecttype; result->server = NULL; result->base_server = NULL; _Py_NewReference(result); return result; } /** ** conn_dealloc ** * */ static void conn_dealloc(connobject *self) { Py_XDECREF(self->server); Py_XDECREF(self->base_server); free(self); } /** ** conn_getattr ** * Get conn object attributes * */ static PyObject * conn_getattr(connobject *self, char *name) { if (strcmp(name, "server") == 0) { /* server serverobject is created as needed */ if (self->server == NULL) { if (self->conn->server == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->server = (PyObject *)make_serverobject(self->conn->server); Py_INCREF(self->server); return self->server; } } else { Py_INCREF(self->server); return self->server; } } else if (strcmp(name, "base_server") == 0) { /* base_server serverobject is created as needed */ if (self->base_server == NULL) { if (self->conn->base_server == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->base_server = (PyObject *)make_serverobject(self->conn->base_server); Py_INCREF(self->base_server); return self->base_server; } } else { Py_INCREF(self->base_server); return self->base_server; } } else return PyMember_Get((char *)self->conn, conn_memberlist, name); } /******************************** *** end of conn object *** ********************************/ /******************************** request object ********************************/ /* * This is a mapping of a Python object to an Apache request_rec. * */ /** ** make_requestobject ** * This routine creates a Python requestobject given an Apache * request_rec pointer. * */ static requestobject * make_requestobject(request_rec *req) { requestobject *result; result = PyMem_NEW(requestobject, 1); if (! result) return (requestobject *) PyErr_NoMemory(); result->request_rec = req; result->ob_type = &requestobjecttype; result->connection = NULL; result->server = NULL; result->next = NULL; result->prev = NULL; result->main = NULL; result->headers_in = make_tableobject(req->headers_in); result->headers_out = make_tableobject(req->headers_out); result->err_headers_out = make_tableobject(req->err_headers_out); result->subprocess_env = make_tableobject(req->subprocess_env); result->notes = make_tableobject(req->notes); result->header_sent = 0; _Py_NewReference(result); ap_register_cleanup(req->pool, (PyObject *)result, python_decref, noop); return result; } /** ** request_dealloc ** * */ static void request_dealloc(requestobject *self) { Py_XDECREF(self->connection); Py_XDECREF(self->server); Py_XDECREF(self->next); Py_XDECREF(self->prev); Py_XDECREF(self->main); Py_XDECREF(self->headers_in); Py_XDECREF(self->headers_out); Py_XDECREF(self->err_headers_out); Py_XDECREF(self->subprocess_env); Py_XDECREF(self->notes); free(self); } /** ** request_getattr ** * Get request object attributes * */ static PyObject * request_getattr(requestobject *self, char *name) { PyObject *res; res = Py_FindMethod(requestobjectmethods, (PyObject *)self, name); if (res != NULL) return res; PyErr_Clear(); if (strcmp(name, "connection") == 0) { if (self->connection == NULL) { if (self->request_rec->connection == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->connection = (PyObject *)make_connobject(self->request_rec->connection); Py_INCREF(self->connection); return self->connection; } } else { Py_INCREF(self->connection); return self->connection; } } else if (strcmp(name, "server") == 0) { if (self->server == NULL) { if (self->request_rec->server == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->server = (PyObject *)make_serverobject(self->request_rec->server); Py_INCREF(self->server); return self->server; } } else { Py_INCREF(self->server); return self->server; } } else if (strcmp(name, "next") == 0) { if (self->next == NULL) { if (self->request_rec->next == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->next = (PyObject *)make_requestobject(self->request_rec->next); Py_INCREF(self->next); return self->next; } } else { Py_INCREF(self->next); return self->next; } } else if (strcmp(name, "prev") == 0) { if (self->prev == NULL) { if (self->request_rec->prev == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->prev = (PyObject *)make_requestobject(self->request_rec->prev); Py_INCREF(self->prev); return self->prev; } } else { Py_INCREF(self->prev); return self->prev; } } else if (strcmp(name, "main") == 0) { if (self->main == NULL) { if (self->request_rec->main == NULL) { Py_INCREF(Py_None); return Py_None; } else { self->main = (PyObject *)make_requestobject(self->request_rec->main); Py_INCREF(self->main); return self->main; } } else { Py_INCREF(self->main); return self->main; } } else if (strcmp(name, "headers_in") == 0) { Py_INCREF(self->headers_in); return (PyObject *) self->headers_in; } else if (strcmp(name, "headers_out") == 0) { Py_INCREF(self->headers_out); return (PyObject *) self->headers_out; } else if (strcmp(name, "err_headers_out") == 0) { Py_INCREF(self->err_headers_out); return (PyObject *) self->err_headers_out; } else if (strcmp(name, "subprocess_env") == 0) { Py_INCREF(self->subprocess_env); return (PyObject *) self->subprocess_env; } else if (strcmp(name, "notes") == 0) { Py_INCREF(self->notes); return (PyObject *) self->notes; } else return PyMember_Get((char *)self->request_rec, request_memberlist, name); } /** ** request_setattr ** * Set request object attributes * */ static int request_setattr(requestobject *self, char *name, PyObject *value) { if (value == NULL) { PyErr_SetString(PyExc_AttributeError, "can't delete request attributes"); return -1; } else if (strcmp(name, "content_type") == 0) { self->request_rec->content_type = PyString_AS_STRING(value); return NULL; } return PyMember_Set((char *)self->request_rec, request_memberlist, name, value); } /** ** request.send_http_header(request self) ** * sends headers, same as ap_send_http_header */ static PyObject * req_send_http_header(requestobject *self, PyObject *args) { if (! self->header_sent) { ap_send_http_header(self->request_rec); self->header_sent = 1; } Py_INCREF(Py_None); return Py_None; } /** ** request.get_basic_auth_pw(request self) ** * get basic authentication password, * similat to ap_get_basic_auth_pw */ static PyObject * req_get_basic_auth_pw(requestobject *self, PyObject *args) { const char *pw; request_rec *req; req = self->request_rec; if (! ap_get_basic_auth_pw(req, &pw)) return PyString_FromString(pw); else { Py_INCREF(Py_None); return Py_None; } } /** ** request.write(request self, string what) ** * write output to the client */ static PyObject * req_write(requestobject *self, PyObject *args) { int len; int rc; char *buff; if (! PyArg_ParseTuple(args, "s#", &buff, &len)) return NULL; /* bad args */ ap_rwrite(buff, len, self->request_rec); rc = ap_rflush(self->request_rec); if (rc == EOF) { PyErr_SetString(PyExc_IOError, "Write failed, client closed connection."); return NULL; } Py_INCREF(Py_None); return Py_None; } /** ** request.read(request self, int bytes) ** * Reads stuff like POST requests from the client * (based on the old net_read) */ static PyObject * req_read(requestobject *self, PyObject *args) { int len, rc, bytes_read; char *buffer; PyObject *result; if (! PyArg_ParseTuple(args, "i", &len)) return NULL; if (len == 0) { return PyString_FromString(""); } else if (len < 0) { PyErr_SetString(PyExc_ValueError, "must have positive integer parameter"); return NULL; } /* is this the first read? */ if (! self->request_rec->read_length) { /* then do some initial setting up */ rc = ap_setup_client_block(self->request_rec, REQUEST_CHUNKED_ERROR); if(rc != OK) { PyErr_SetObject(PyExc_IOError, PyInt_FromLong(rc)); return NULL; } if (! ap_should_client_block(self->request_rec)) { /* client has nothing to send */ Py_INCREF(Py_None); return Py_None; } } result = PyString_FromStringAndSize(NULL, len); /* possibly no more memory */ if (result == NULL) return NULL; /* read it in */ buffer = PyString_AS_STRING((PyStringObject *) result); bytes_read = ap_get_client_block(self->request_rec, buffer, len); /* resize if necessary */ if (bytes_read < len) if(_PyString_Resize(&result, bytes_read)) return NULL; return result; } /** ** request.get_config(request self) ** * Returns the config directives set through Python* apache directives. * except for PythonOption, which you get via get_options */ static PyObject * req_get_config(requestobject *self, PyObject *args) { py_dir_config *conf = (py_dir_config *) ap_get_module_config(self->request_rec->per_dir_config, &python_module); return (PyObject *)make_tableobject(conf->directives); } /** ** request.get_options(request self) ** */ static PyObject * req_get_options(requestobject *self, PyObject *args) { py_dir_config *conf = (py_dir_config *) ap_get_module_config(self->request_rec->per_dir_config, &python_module); return (PyObject *)make_tableobject(conf->options); } /** ** request.get_config(request self) ** * Returns a table keyed by directives with the last path in which the * directive was encountered. */ static PyObject * req_get_dirs(requestobject *self, PyObject *args) { py_dir_config *conf = (py_dir_config *) ap_get_module_config(self->request_rec->per_dir_config, &python_module); return (PyObject *)make_tableobject(conf->dirs); } /** ** request.add_common_vars(reqeust self) ** * Interface to ap_add_common_vars. Adds a bunch of CGI * environment variables. * */ static PyObject * req_add_common_vars(requestobject *self, PyObject *args) { ap_add_common_vars(self->request_rec); Py_INCREF(Py_None); return Py_None; } /******************************** *** end of request object *** ********************************/ /** ** table2dict() ** * Converts Apache table to Python dictionary. */ static PyObject * table2dict(table * t) { PyObject *result, *val; array_header *ah; table_entry *elts; int i; result = PyDict_New(); ah = ap_table_elts (t); elts = (table_entry *) ah->elts; i = ah->nelts; while (i--) if (elts[i].key) { val = PyString_FromString(elts[i].val); PyDict_SetItemString(result, elts[i].key, val); Py_DECREF(val); } return result; /* prevent compiler warning about unused function, this statement never reached */ table2dict(NULL); } /** ** python_dict_merge ** * Merges two Python dictionaries into one, overlaying * items in the first argument by the second. Returns new * reference. */ void python_dict_merge(PyObject *d1, PyObject *d2) { /* * there are faster ways to do this if we were to bypas * the Python API and use functions from dictobject.c * directly */ PyObject *items; PyObject *keyval; PyObject *x; PyObject *y; int i; items = PyDict_Items(d2); for (i = 0; i < PyList_Size(items); i++) { keyval = PyList_GetItem(items, i); x = PyTuple_GetItem(keyval, 0); y = PyTuple_GetItem(keyval, 1); printf("X Y: %d %d\n", x->ob_refcnt, y->ob_refcnt); PyDict_SetItem(d1, PyTuple_GetItem(keyval, 0), PyTuple_GetItem(keyval, 1)); printf("X Y: %d %d\n", x->ob_refcnt, y->ob_refcnt); } Py_DECREF(items); printf("X Y: %d %d\n", x->ob_refcnt, y->ob_refcnt); } /****************************************************************** *** end of Python objects and their methods *** ******************************************************************/ /****************************************************************** functions called by Apache or Python ******************************************************************/ /** ** python_decref ** * This function is used with ap_register_cleanup to destroy * python objects when a certain pool is destroyed. */ void python_decref(void *object) { if (debug) { PyObject *s, *s2; s = PyObject_Type(object); s2 = PyObject_Repr(s); printf("python_decref(): decrementing %s at %d\n", PyString_AsString(s2), (int) object); Py_DECREF(s2); Py_DECREF(s); } Py_XDECREF((PyObject *) object); } /** ** python_init() ** * Called at Apache mod_python initialization time. */ void python_init(server_rec *s, pool *p) { char buff[255]; PyObject *obcallback = NULL; PyThreadState *tstate = NULL; PyObject *x; /* initialize serverobjecttype */ PyTypeObject sot = { PyObject_HEAD_INIT(&PyType_Type) 0, "server", sizeof(serverobject), 0, (destructor) server_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) server_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; /* initialize connobjecttype */ PyTypeObject cot = { PyObject_HEAD_INIT(&PyType_Type) 0, "conn", sizeof(connobject), 0, (destructor) conn_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) conn_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; /* initialize requestobjecttype */ PyTypeObject rot = { PyObject_HEAD_INIT(&PyType_Type) 0, "request", sizeof(requestobject), 0, (destructor) request_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) request_getattr, /*tp_getattr*/ (setattrfunc) request_setattr, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ }; /* initialize tableobjecttype */ PyTypeObject tt = { PyObject_HEAD_INIT(&PyType_Type) 0, "mptable", sizeof(tableobject), 0, (destructor) table_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc) table_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc) table_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ &table_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ }; serverobjecttype = sot; connobjecttype = cot; requestobjecttype = rot; tableobjecttype = tt; if (debug) printf("python_init(): adding version component..."); /* mod_python version */ ap_add_version_component(VERSION_COMPONENT); /* Python version */ sprintf(buff, "Python/%s", strtok((char *)Py_GetVersion(), " ")); ap_add_version_component(buff); if (debug) printf("python_init(): initializing..."); /* initialize global Python interpreter if necessary */ if (! Py_IsInitialized()) { if (debug) printf("python_init(): calling Py_Initialize()\n"); /* initialze the interpreter */ Py_Initialize(); #ifdef WITH_THREAD /* create and acquire the interpreter lock */ PyEval_InitThreads(); #endif if (debug) printf("python_init(): creating new interpreters dictionary\n"); /* create the obCallBack dictionary */ interpreters = PyDict_New(); if (! interpreters) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, "python_init: PyDict_New() failed! No more memory?"); exit(1); } /* Initialize _apache. * This makes an _apache module available for import, but remember, * the user should use "import apache" not "_apache". "_apache" * is for internal use only. (The only time it's needed is to assign * obCallBack) */ if (debug) printf("python_init(): calling Py_Init Module() for _apache\n"); /* make obCallBack */ obcallback = make_obcallback(NULL, NULL); /* get the current thread state */ tstate = PyThreadState_Get(); if (debug) printf("python_init(): saving PyThreadState %d in obcallback\n", (int) tstate); /* cast PyThreadState * to long and save it in obCallBack */ x = PyInt_FromLong((long) tstate); PyObject_SetAttrString(obcallback, INTERP_ATTR, x); Py_DECREF(x); if (debug) printf("python_init(): saving obcallback in interpreters under \"global_interpreter\"\n"); /* save the obCallBack */ PyDict_SetItemString(interpreters, "global_interpreter", obcallback); #ifdef WITH_THREAD if (debug) printf("python_init(): calling PyEval_ReleaseLock()\n"); /* release the lock; now other threads can run */ PyEval_ReleaseLock(); #endif } } /** ** python_create_dir_config ** * Allocate memory and initialize the strucure that will * hold configuration parametes. * * This function is called on every hit it seems. */ static void *python_create_dir_config(pool *p, char *dir) { py_dir_config *conf = (py_dir_config *) ap_pcalloc(p, sizeof(py_dir_config)); if (debug) printf("python_create_dir_config(): %s\n", (dir != NULL) ? dir : "(null)"); conf->authoritative = 1; conf->config_dir = ap_pstrdup(p, dir); conf->options = ap_make_table(p, 4); conf->directives = ap_make_table(p, 4); conf->dirs = ap_make_table(p, 4); return conf; } /** ** tuple_from_array_header ** * Given an array header return a tuple. The array elements * assumed to be strings. */ PyObject * tuple_from_array_header(const array_header *ah) { PyObject *t; int i; char *s; if (ah == NULL) { Py_INCREF(Py_None); return Py_None; } else { t = PyTuple_New(ah->nelts); s = (char *) ah->elts; for (i = 0; i < ah->nelts; i++) PyTuple_SetItem(t, i, PyString_FromString(&s[i])); return t; } } /** ** python_merge_dir_config ** */ static void *python_merge_dir_config(pool *p, void *cc, void *nc) { py_dir_config *merged_conf = (py_dir_config *) ap_pcalloc(p, sizeof(py_dir_config)); py_dir_config *current_conf = (py_dir_config *) cc; py_dir_config *new_conf = (py_dir_config *) nc; if (debug) printf("python_merge_dir_config(): cc->config_dir %s nc->config_dir %s\n", (! current_conf->config_dir) ? "null" : current_conf->config_dir , (! new_conf->config_dir) ? "null" : new_conf->config_dir); /* we basically allow the local configuration to override global, * by first copying current values and then new values on top */ /** create **/ merged_conf->directives = ap_make_table(p, 4); merged_conf->dirs = ap_make_table(p, 16); merged_conf->options = ap_make_table(p, 16); /** copy current **/ merged_conf->authoritative = current_conf->authoritative; merged_conf->config_dir = ap_pstrdup(p, current_conf->config_dir); copy_table(merged_conf->directives, current_conf->directives); copy_table(merged_conf->dirs, current_conf->dirs); copy_table(merged_conf->options, current_conf->options); /** copy new **/ if (new_conf->authoritative != merged_conf->authoritative) merged_conf->authoritative = new_conf->authoritative; if (new_conf->config_dir) merged_conf->config_dir = ap_pstrdup(p, new_conf->config_dir); copy_table(merged_conf->directives, new_conf->directives); copy_table(merged_conf->dirs, new_conf->dirs); copy_table(merged_conf->options, new_conf->options); return (void *) merged_conf; } /** ** python_directive ** * Called by Python*Handler directives. */ static const char *python_directive(cmd_parms *cmd, void * mconfig, char *key, const char *val) { int directs_offset = XtOffsetOf(py_dir_config, directives); int dirs_offset = XtOffsetOf(py_dir_config, dirs); int config_dir_offset = XtOffsetOf(py_dir_config, config_dir); char *s; table *directives; table *dirs; if (debug) printf("python_directive(): %s: %s\n", key, val); directives = *(table **)(mconfig + directs_offset); dirs = *(table **)(mconfig + dirs_offset); ap_table_set(directives, ap_pstrdup(cmd->pool, key), ap_pstrdup(cmd->pool, val)); /* remember the directory where the directive was found */ s = *(char **)(mconfig + config_dir_offset); if (s) { ap_table_set(dirs, ap_pstrdup(cmd->pool, key), s); } else { ap_table_set(dirs, ap_pstrdup(cmd->pool, key), ""); } return NULL; } /** ** make_obcallback ** * This function instantiates an obCallBack object. * NOTE: The obCallBack object is instantiated by Python * code. This C module calls into Python code which sets the * reference by calling SetCallBack function back into C from * Python. Thus the name "CallBack". * * Here is a visual: * * - Begin C code * . PyRun_SimpleString("init()") * = Begin Python code * . obCallBack = CallBack() * . import _apache # this module is in C * . _apache.SetCallBack(obCallBack) * -- Begin C code * . // assigns to global obCallBack * -- End of C code * = End of Python code * - we're back in C, but now global obCallBack has a value */ PyObject * make_obcallback(const char *module, const char *initstring) { char buff[256]; /* In the old verion of this module these varuables were assigned by * PythonInitFunction directive, in the new version they'll be * NULL and get assigned here. */ if (! module) module = MODULENAME; if (! initstring) initstring = INITSTRING; if (debug) printf("make_obcallback(): initializing _apache...\n"); /* This makes _apache appear imported, and subsequent * >>> import _apache * will not give an error. */ Py_InitModule("_apache", _apache_module_methods); if (debug) printf("make_obcallback(): making callback obj with module %s initstring %s\n", module, initstring); /* Now execute the equivalent of * >>> import sys * >>> import * >>> * in the __main__ module to start up Python. */ if (debug) printf("make_obcallback(): >>> import %s\n", module); sprintf(buff, "import %s\n", module); if (PyRun_SimpleString(buff)) { fprintf(stderr, "PythonInitFunction: could not import %s.\n", module); exit(1); } if (debug) printf("make_obcallback(): >>> %s\n", initstring); sprintf(buff, "%s\n", initstring); if (PyRun_SimpleString(buff)) { fprintf(stderr, "PythonInitFunction: could not call %s.\n", initstring); exit(1); } /* we can't see it, but the execution has moved into a Python script * which is doing something like this: * * >>> import _apache * >>> _apache.SetCallBack(someCallBackObject) * * By now (i.e while you're reading this comment), * SetCallBack() has been called and obCallBack is assigned. */ if (! obCallBack) { fprintf(stderr, "PythonInitFunction: after %s no callback object found.\n", initstring); exit(1); } /* Wow, this worked! */ if (debug) printf("make_obcallback(): callback obj made successfully!\n"); return obCallBack; } /** ** get_obcallback ** * We have a list of obCallBack's, one per each instance of * interpreter we use. * * This function gets an obCallBack instance: * 1. It checks if obCallBack with such name already exists * 2. If yes - return it * 3. If no - create one doing Python initialization * * Lock must be acquired prior to entering this function. * This function might make the resulting interpreter's state current, * use PyThreadState_Swap to be sure. * * name NULL means use global interpreter. */ PyObject * get_obcallback(const char *name, server_rec * server) { PyObject * obcallback = NULL; PyThreadState * tstate = NULL; PyObject *p; if (! name) name = "global_interpreter"; /* * Note that this is somewhat of a hack because to store * PyThreadState pointers in a Python object we are * casting PyThreadState * to an integer and back. * * The bad news is that one cannot cast a * to int on systems * where sizeof(void *) != sizeof(int). * * The good news is that I don't know of any systems where * sizeof(void *) != sizeof(int) except for 16 bit DOS. * * The proper way to do this would be to write a separate * hashable Python type that contains a PyThreadState. At this * point it doesn't seem worth the trouble. */ /* see if one exists by that name */ obcallback = PyDict_GetItemString(interpreters, (char *) name); /* if obcallback is NULL at this point, no such interpeter exists */ if (! obcallback) { if (debug) printf("get_obcallback(): no interprter %s exists, calling Py_NewInterpreter()\n", name); /* create a new interpeter */ tstate = Py_NewInterpreter(); if (! tstate) { /* couldn't create an interpreter, this is bad */ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server, "get_obcallback: Py_NewInterpreter() returned NULL. No more memory?"); return NULL; } /* create an obCallBack */ obcallback = make_obcallback(NULL, NULL); if (debug) printf("get_obcallback(): saving interpreter %s (%d) in interprters\n", name, (int) tstate); /* cast PyThreadState * to long and save it in obCallBack */ p = PyInt_FromLong((long) tstate); PyObject_SetAttrString(obcallback, INTERP_ATTR, p); Py_DECREF(p); /* save the obCallBack */ PyDict_SetItemString(interpreters, (char *)name, obcallback); } return obcallback; } /** ** PythonInitFunction ** * NOTE: This function and directive are obsolete! * * When Apache sees "PythonInitFunction" in httpd.conf, it * calls this function. PythonInitFunction makes everything * function in "sinlge interpreter" mode for backwards compatibility. * * This function will destroy the obCallBack created by * python_init and creates a new one with new parameters. */ static const char *directive_PythonInitFunction(cmd_parms *cmd, void *dummy, const char *module, const char *initstring) { PyObject *obcallback; PyObject *p; long l_tstate; /* this function can only be called after python_init */ if (! Py_IsInitialized()) { if (debug) printf ("directive_PythonInitFunction(): postponing until Python initialized...\n" ); /* postpone */ return NULL; } /* make sure the directive is syntactically correct */ if (!module) { fprintf(stderr, "PythonInitFunction: no module specified"); exit(1); } if (!initstring) { fprintf(stderr, "PythonInitFunction: no initstring specified"); exit(1); } if (debug) printf("directive_PythonInitFunction(): entering sinlge interpreter mode\n"); /* we are in sinlge interpreter mode now */ single_mode = 1; #ifdef WITH_THREAD if (debug) printf("directive_PythonInitFunction(): calling PyEval_AcquireLock()\n"); /* Acquire lock */ PyEval_AcquireLock(); #endif /* get the obCallBack for global_interpreter */ obcallback = PyDict_GetItemString(interpreters, "global_interpreter"); if (! obcallback) { fprintf(stderr, "PythonInitFunction: global_interpreter obCallBack not found! Weird, bailing!"); exit(1); } /* save the pointer to the tstate (__interpreter__ attribute) */ p = PyObject_GetAttrString(obcallback, INTERP_ATTR); l_tstate = PyInt_AsLong(p); Py_DECREF(p); if (debug) printf("directive_PythonInitFunction(): keeping tstate %ld, destroying current obCallBack\n", l_tstate); /* destroy the current obCallBack */ Py_XDECREF(obcallback); /* create a new obCallBack */ obcallback = make_obcallback(module, initstring); if (debug) printf("directive_PythonInitFunction(): saving tstate in new obCallBack\n"); /* set the pointer to the tstate (__interpreter__ attribute) */ p = PyInt_FromLong(l_tstate); PyObject_SetAttrString(obcallback, INTERP_ATTR, p); Py_DECREF(p); if (debug) printf("directive_PythonInitFunction(): saving new obCallBack in interpreters\n"); /* save the new obCallBack */ PyDict_SetItemString(interpreters, "global_interpreter", obcallback); #ifdef WITH_THREAD if (debug) printf("directive_PythonInitFunction(): calling PyEval_ReleaseLock()\n"); /* Release lock */ PyEval_ReleaseLock(); #endif return NULL; } /** ** log_error ** * A wrpapper to ap_log_error * * log_error(string message, int level, server server) * */ static PyObject * log_error(PyObject *self, PyObject *args) { int level = 0; char *message = NULL; serverobject *server = NULL; server_rec *serv_rec; if (! PyArg_ParseTuple(args, "z|iO", &message, &level, &server)) return NULL; /* error */ if (message) { if (! level) level = APLOG_NOERRNO|APLOG_ERR; if (!server) serv_rec = NULL; else { if (! is_serverobject(server)) { PyErr_BadArgument(); return NULL; } serv_rec = server->server; } ap_log_error(APLOG_MARK, level, serv_rec, message); } Py_INCREF(Py_None); return Py_None; } /** ** SetCallBack - assign a CallBack object ** * This function must be called from Python upon executing * the initstring parameter, like this * * >>> import _apache * >>> _apache.SetCallBack(instance) * * to provide the instance as the CallBack object. */ static PyObject * SetCallBack(PyObject *self, PyObject *args) { PyObject *callback; /* see if CallBack is in *args */ if (! PyArg_ParseTuple(args, "O", &callback)) return NULL; if (debug) printf("SetCallBack(): we are being provided a obCallBack instance of %d\n", (int) callback); /* store the object, incref */ obCallBack = callback; Py_INCREF(obCallBack); /* return None */ Py_INCREF(Py_None); return Py_None; } /** ** get_request_object ** * This creates or retrieves a previously created request object */ static void get_request_object(request_rec *req, requestobject **request_obj) { char *s; char s2[40]; /* see if there is a request object out there already */ /* XXX there must be a cleaner way to do this, atoi is slow? */ /* since tables only understand strings, we need to do some conversion */ s = (char *) ap_table_get(req->notes, "python_request_ptr"); if (s) { *request_obj = (requestobject *) atoi(s); /* we have one reference at creation that is shared, no INCREF here*/ return; } else { /* ap_add_cgi_vars() is necessary for path-translated for example */ /* ap_add_cgi_vars() sometimes recurses, so we temporarily release the lock */ /* XXX - why do I need this? Still true as of ver 1.3.12. * A trailing slash will make Apache call add_cgi_vars * recursively ad infinitum in some handlers. May be it's an Apache bug? */ if ((req->path_info) && (req->path_info[strlen(req->path_info) - 1] == '/')) { int i; i = strlen( req->path_info ); /* take out the slash */ req->path_info[ i - 1 ] = 0; Py_BEGIN_ALLOW_THREADS; ap_add_cgi_vars(req); Py_END_ALLOW_THREADS; *request_obj = make_requestobject(req); /* put the slash back in */ req->path_info[ i - 1 ] = '/'; req->path_info[ i ] = 0; } else { Py_BEGIN_ALLOW_THREADS; ap_add_cgi_vars(req); Py_END_ALLOW_THREADS; *request_obj = make_requestobject(req); } /* store the pointer to this object in notes */ sprintf(s2, "%d", (int) *request_obj); ap_table_set(req->notes, "python_request_ptr", s2); } } /** ** python_handler ** * A generic python handler. Most handlers should use this. */ static int python_handler(request_rec *req, char *handler) { PyObject *resultobject = NULL; PyThreadState *tstate; PyObject *obcallback; requestobject *request_obj; const char *s; py_dir_config * conf; int result; const char * interpreter = NULL; if (debug) printf("python_handler(): %s: req->filename %s\n", handler, req->filename); /* get configuration */ conf = (py_dir_config *) ap_get_module_config(req->per_dir_config, &python_module); /* is there a handler? */ if (! ap_table_get(conf->directives, handler)) { if (debug) printf("python_handler(): %s: no handler, declining.\n", handler); return DECLINED; } /* determine interpreter to use */ if ((s = ap_table_get(conf->directives, "PythonInterpreter"))) { /* forced by configuration */ interpreter = s; } else { if (single_mode) interpreter = NULL; else { /* base interpreter name on directory where the handler directive * was last found. If it was in http.conf, then we will use the global * interpreter. */ s = ap_table_get(conf->dirs, handler); if (strcmp(s, "") == NULL) interpreter = NULL; else interpreter = s; } } if (debug) { printf("python_handler(): will use interpreter %s.\n", interpreter); #ifdef WITH_THREAD printf("python_handler(): calling PyEval_AcquireLock()...\n"); #endif } #ifdef WITH_THREAD /* acquire lock */ PyEval_AcquireLock(); #endif if (debug) { /* lots of debugging info */ printf("python_handler(): %s, configuration in effect *here*:\n", handler); if (interpreter) printf(" interpreter: \t%s\n", interpreter); print_table(conf->directives); print_table(conf->dirs); print_table(conf->options); } /* get/create obcallback */ obcallback = get_obcallback(interpreter, req->server); /* we must have a callback object to succeed! */ if (!obcallback) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, req->server, "python_handler: get_obcallback returned no obCallBack!"); return HTTP_INTERNAL_SERVER_ERROR; } /* find __interpreter__ in obCallBack */ tstate = (PyThreadState *) PyInt_AsLong(PyObject_GetAttrString(obcallback, INTERP_ATTR)); if (debug) printf("python_handler(): got tstate %d, making it current with PyThreadState_Swap()\n", (int) tstate); /* make this thread state current */ PyThreadState_Swap(tstate); /* create/acquire request object */ get_request_object(req, &request_obj); /* XXX This MUST be noted in the documentation clearly! * The current directory will be that of the last encountered * Python*Handler directive. If the directive was in httpd.conf, * then the directory is null, and cwd could be anything (most * likely serverroot). */ if ((s = ap_table_get(conf->dirs, handler))) chdir(s); /* * Here is where we call into Python! * This is the C equivalent of * >>> resultobject = obCallBack.Service(pbo, sno, rqo) */ if (debug) printf("python_handler(): %s: calling PyObject_CallMethod()\n", handler); resultobject = PyObject_CallMethod(obcallback, "Dispatch", "Os", request_obj, handler); #ifdef WITH_THREAD if (debug) printf("python_handler(): %s: user code done.\n", handler); /* release the lock */ PyEval_ReleaseLock(); #endif if (! resultobject) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, req->server, "python_handler: Service() returned nothing."); return HTTP_INTERNAL_SERVER_ERROR; } else { /* Attempt to analyse the result as a string indicating which result to return */ if (! PyInt_Check(resultobject)) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, req->server, "python_handler: Service() returned non-integer."); return HTTP_INTERNAL_SERVER_ERROR; } else { result = PyInt_AsLong(resultobject); } } /* When the script sets an error status by using req.status, * it can then either provide its own HTML error message or have * Apache provide one. To have Apache provide one, you need to send * no output and return the error from the handler function. However, * if the script is providing HTML, then the return value of the * handler should be OK, else the user will get both the script * output and the Apache output. */ /* Another note on status. req->status is used to build req->status_line * unless status_line is not NULL. req->status has no effect on how the * server will behave. The error behaviour is dictated by the return * value of this handler. When the handler returns anything other than OK, * the server will display the error that matches req->status, unless it is * 200 (HTTP_OK), in which case it will just show the error matching the return * value. If the req->status and the return of the handle do not match, * then the server will first show what req->status shows, then it will * print "Additionally, X error was recieved", where X is the return code * of the handle. If the req->status or return code is a weird number that the * server doesn't know, it will default to 500 Internal Server Error. */ /* if error status was specified, result is OK and no * content provided by the script, have Apache provide * error content */ /* if ((req->status) && (req->status != 200) && (req->bytes_sent == 0) && (result == OK)) { result = req->status; req->status = 200; } */ if (debug) printf("python_handler(): result %d, req->status %d, req->status_line is %s, bytes_sent %ld\n", result, req->status, req->status_line, req->bytes_sent); /* clean up */ Py_XDECREF(resultobject); /* return the translated result (or default result) to the Server. */ return result; } /** ** directive_PythonInterpreter ** * This function called whenever PythonInterpreter directive * is encountered. */ static const char *directive_PythonInterpreter(cmd_parms *cmd, void *mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonInterpreter", val); } /** ** directive_PythonOption ** * This function is called every time PythonOption directive * is encountered. It sticks the option into a table containing * a list of options. This table is part of the local config structure. */ static const char *directive_PythonOption(cmd_parms *cmd, void * mconfig, const char * key, const char * val) { int offset = XtOffsetOf(py_dir_config, options); table * options; if (debug) printf("directive_PythonOption(): key: %s val: %s\n", key, val); options = * (table **) (mconfig + offset); ap_table_set(options, key, val); return NULL; } /** ** Python*Handler directives ** */ static const char *directive_PythonPostReadRequestHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonPostReadRequestHandler", val); } static const char *directive_PythonTransHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonTransHandler", val); } static const char *directive_PythonHeaderParserHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonHeaderParserHandler", val); } static const char *directive_PythonAccessHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonAccessHandler", val); } static const char *directive_PythonAuthenHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonAuthenHandler", val); } static const char *directive_PythonAuthzHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonAuthzHandler", val); } static const char *directive_PythonTypeHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonTypeHandler", val); } static const char *directive_PythonHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonHandler", val); } static const char *directive_PythonFixupHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonFixupHandler", val); } static const char *directive_PythonLogHandler(cmd_parms *cmd, void * mconfig, const char *val) { return python_directive(cmd, mconfig, "PythonLogHandler", val); } /** ** Handlers ** * (In order, in which they get called by Apache) */ static int PythonPostReadRequestHandler(request_rec *req) { return python_handler(req, "PythonPostReadRequestHandler"); } static int PythonTransHandler(request_rec *req) { return python_handler(req, "PythonTransHandler"); } static int PythonHeaderParserHandler(request_rec *req) { return python_handler(req, "PythonHeaderParserHandler"); } static int PythonAccessHandler(request_rec *req) { return python_handler(req, "PythonAccessHandler"); } static int PythonAuthenHandler(request_rec *req) { return python_handler(req, "PythonAuthenHandler"); } static int PythonAuthzHandler(request_rec *req) { return python_handler(req, "PythonAuthzHandler"); } static int PythonTypeHandler(request_rec *req) { return python_handler(req, "PythonTypeHandler"); } static int PythonHandler(request_rec *req) { return python_handler(req, "PythonHandler"); } static int PythonFixupHandler(request_rec *req) { return python_handler(req, "PythonFixupHandler"); } static int PythonLogHandler(request_rec *req) { return python_handler(req, "PythonLogHandler"); } /****************************************************************** * *** end of functions called by Apache or Python *** ******************************************************************/ /****************************************************************** * Apache module stuff ******************************************************************/ /* content handlers */ static handler_rec python_handlers[] = { { "python-program", PythonHandler }, { NULL } }; /* command table */ command_rec python_commands[] = { { "PythonInitFunction", /* directive name */ directive_PythonInitFunction, /* config action routine */ NULL, /* argument to include in call */ RSRC_CONF, /* where available */ TAKE2, /* arguments */ "A line of Python to initialize it." /* directive description */ }, { "PythonInterpreter", directive_PythonInterpreter, NULL, OR_ALL, TAKE1, "Forces a specific Python interpreter name to be used here." }, { "PythonOption", directive_PythonOption, NULL, OR_ALL, TAKE2, "Sets specific Httpdapy options." }, { "PythonPostReadRequestHandler", directive_PythonPostReadRequestHandler, NULL, OR_ALL, RAW_ARGS, "Python post read-request handlers." }, { "PythonTransHandler", directive_PythonTransHandler, NULL, OR_ALL, RAW_ARGS, "Python filename to URI translation handlers." }, { "PythonHeaderParserHandler", directive_PythonHeaderParserHandler, NULL, OR_ALL, RAW_ARGS, "Python header parser handlers." }, { "PythonAccessHandler", directive_PythonAccessHandler, NULL, OR_ALL, RAW_ARGS, "Python access by host address handlers." }, { "PythonAuthenHandler", directive_PythonAuthenHandler, NULL, OR_ALL, RAW_ARGS, "Python authentication handlers." }, { "PythonAuthzHandler", directive_PythonAuthzHandler, NULL, OR_ALL, RAW_ARGS, "Python authorization (user allowed _here_) handlers." }, { "PythonTypeHandler", directive_PythonTypeHandler, NULL, OR_ALL, RAW_ARGS, "Python MIME type checker/setter handlers." }, { "PythonHandler", directive_PythonHandler, NULL, OR_ALL, RAW_ARGS, "Python request handlers." }, { "PythonFixupHandler", directive_PythonFixupHandler, NULL, OR_ALL, RAW_ARGS, "Python fixups handlers." }, { "PythonLogHandler", directive_PythonLogHandler, NULL, OR_ALL, RAW_ARGS, "Python logger handlers." }, {NULL} }; /* module definition */ /* XXX * PythonChildInitHandler and PythonChildExitHandler * NOTE - it's not clear which interpreter would those run in * since the interprters exist per-path, and at those stages * there is no request to get the path from.... */ module python_module = { STANDARD_MODULE_STUFF, python_init, /* module initializer */ python_create_dir_config, /* per-directory config creator */ python_merge_dir_config, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ python_commands, /* command table */ python_handlers, /* [7] list of handlers */ PythonTransHandler, /* [2] filename-to-URI translation */ PythonAuthenHandler, /* [5] check/validate user_id */ PythonAuthzHandler, /* [6] check user_id is valid *here* */ PythonAccessHandler, /* [4] check access by host address */ PythonTypeHandler, /* [7] MIME type checker/setter */ PythonFixupHandler, /* [8] fixups */ PythonLogHandler, /* [10] logger */ PythonHeaderParserHandler, /* [3] header parser */ NULL, /* process initializer */ NULL, /* process exit/cleanup */ PythonPostReadRequestHandler /* [1] post read_request handling */ }; /****************************************************************** * LE FIN ******************************************************************/