//***************************************************************************** // // pysocket.c // // Contains a python socket module, and an Socket class that is a python // wrapper for NakedMud sockets. // //***************************************************************************** #include "../mud.h" #include "../utils.h" #include "../socket.h" #include "../character.h" #include "scripts.h" #include "pyplugs.h" #include "pyauxiliary.h" #include "pysocket.h" //***************************************************************************** // mandatory modules //***************************************************************************** #include "../editor/editor.h" #include "script_editor.h" //***************************************************************************** // local structures and defines //***************************************************************************** // a list of the get/setters on the Exit class LIST *pysocket_getsetters = NULL; // a list of the methods on the Exit class LIST *pysocket_methods = NULL; typedef struct { PyObject_HEAD int uid; } PySocket; //***************************************************************************** // allocation, deallocation, initialization, and comparison //***************************************************************************** void PySocket_dealloc(PySocket *self) { self->ob_type->tp_free((PyObject*)self); } PyObject *PySocket_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PySocket *self; self = (PySocket *)type->tp_alloc(type, 0); self->uid = NOTHING; return (PyObject *)self; } int PySocket_init(PySocket *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {"uid", NULL}; int uid = NOTHING; // get the uid if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &uid)) { PyErr_Format(PyExc_TypeError, "Sockets may only be created using an uid"); return -1; } // make sure an socket with this name exists if(!propertyTableGet(sock_table, uid)) { PyErr_Format(PyExc_TypeError, "Socket with uid, %d, does not exist", uid); return -1; } self->uid = uid; return 0; } int PySocket_compare(PySocket *sock1, PySocket *sock2) { if(sock1->uid == sock2->uid) return 0; else if(sock1->uid < sock2->uid) return -1; else return 1; } //***************************************************************************** // getters and setters for the Socket class //***************************************************************************** PyObject *PySocket_getuid(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock != NULL) return Py_BuildValue("i", socketGetUID(sock)); else return NULL; } PyObject *PySocket_getaccount(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else { ACCOUNT_DATA *acc = socketGetAccount(sock); if(acc == NULL) return Py_BuildValue("O", Py_None); return Py_BuildValue("O", accountGetPyFormBorrowed(acc)); } } PyObject *PySocket_getchar(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else { CHAR_DATA *ch = socketGetChar(sock); // for the time being, we don't return characters without UIDs... like ones // that are being created. We have to redo character generation to allow // for characters-in-progress to be referenced if(ch == NULL || charGetUID(ch) == NOBODY) return Py_BuildValue("O", Py_None); return Py_BuildValue("O", charGetPyFormBorrowed(ch)); } } PyObject *PySocket_getcmdread(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else { return Py_BuildValue("i", socketHasCommand(sock)); } } PyObject *PySocket_get_outbound_text(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else { return Py_BuildValue("s", bufferString(socketGetOutbound(sock))); } } int PySocket_set_outbound_text(PySocket *self, PyObject *value, void *closure) { if(!PyString_Check(value)) { PyErr_Format(PyExc_TypeError, "Outbound text must be in string format."); return -1; } SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return -1; else { bufferClear(socketGetOutbound(sock)); bufferCat(socketGetOutbound(sock), PyString_AsString(value)); return 0; } } PyObject *PySocket_get_can_use(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else return Py_BuildValue("i", (socketGetDNSLookupStatus(sock) == TSTATE_DONE ? TRUE : FALSE)); } PyObject *PySocket_getstate(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else return Py_BuildValue("s", socketGetState(sock)); } PyObject *PySocket_getidletime(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else return Py_BuildValue("f", socketGetIdleTime(sock)); } PyObject *PySocket_gethostname(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; else if(socketGetDNSLookupStatus(sock) == TSTATE_DONE) return Py_BuildValue("s", socketGetHostname(sock)); else return Py_BuildValue("s", "unresolved"); } PyObject *PySocket_getcols(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock != NULL) return Py_BuildValue("i", socketGetCols(sock)); else return NULL; } PyObject *PySocket_getrows(PySocket *self, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock != NULL) return Py_BuildValue("i", socketGetRows(sock)); else return NULL; } int PySocket_setcols(PyObject *self, PyObject *value, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return -1; else { if(value == NULL || value == Py_None) socketSetCols(sock, SCREEN_WIDTH); else if(PyInt_Check(value)) socketSetCols(sock, (int)PyInt_AsLong(value)); else { PyErr_Format(PyExc_TypeError, "Tried to change socket %d's screen width to an invalid type.", socketGetUID(sock)); return -1; } return 0; } } int PySocket_setrows(PyObject *self, PyObject *value, void *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return -1; else { if(value == NULL || value == Py_None) socketSetRows(sock, NUM_LINES_PER_PAGE); else if(PyInt_Check(value)) socketSetRows(sock, (int)PyInt_AsLong(value)); else { PyErr_Format(PyExc_TypeError, "Tried to change socket %d's screen height to an invalid type.", socketGetUID(sock)); return -1; } return 0; } } PyObject *PySocket_bust_prompt(PySocket *self, PyObject *closure) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) { PyErr_Format(PyExc_StandardError, "Tried to bust prompt on nonexistent " "socket, %d.", PySocket_AsUid((PyObject *)self)); return NULL; } socketBustPrompt(sock); return Py_BuildValue("i", 1); } PyObject *PySocket_push_ih(PySocket *self, PyObject *args) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); PyObject *handler = NULL; PyObject *prompt = NULL; char *state = NULL; if(sock == NULL) return NULL; if (!PyArg_ParseTuple(args, "OO|s", &handler, &prompt, &state)) { PyErr_Format(PyExc_TypeError, "handler and prompt function must " "be supplied."); return NULL; } socketPushPyInputHandler(sock, handler, prompt, (state ? state : "")); return Py_BuildValue("i", 1); } PyObject *PySocket_pop_ih(PySocket *self) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; socketPopInputHandler(sock); return Py_BuildValue("i", 1); } PyObject *PySocket_replace_ih(PySocket *self, PyObject *args) { PyObject *retval = PySocket_pop_ih(self); if(retval == NULL) return NULL; Py_DECREF(retval); retval = PySocket_push_ih(self, args); if(retval == NULL) return NULL; Py_DECREF(retval); return Py_BuildValue("i", 1); } PyObject *PySocket_send_rraw(PySocket *self, PyObject *value) { char *mssg = NULL; if (!PyArg_ParseTuple(value, "s", &mssg)) { PyErr_Format(PyExc_TypeError, "Sockets may only be sent strings"); return NULL; } SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock != NULL) { raw_to_socket(sock, "%s", mssg); return Py_BuildValue("i", 1); } else { PyErr_Format(PyExc_TypeError, "Tried to send message to nonexistant socket, %d.", self->uid); return NULL; } } PyObject *PySocket_send_raw(PySocket *self, PyObject *value) { char *mssg = NULL; if (!PyArg_ParseTuple(value, "s", &mssg)) { PyErr_Format(PyExc_TypeError, "Sockets may only be sent strings"); return NULL; } SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock != NULL) { send_to_socket(sock, "%s", mssg); return Py_BuildValue("i", 1); } else { PyErr_Format(PyExc_TypeError, "Tried to send message to nonexistant socket, %d.", self->uid); return NULL; } } // // Send a message with Python statements potentially embedded in it. For //evaluating PyObject *PySocket_send(PyObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[ ] = { "mssg", "dict", "newline", NULL }; SOCKET_DATA *me = NULL; char *text = NULL; PyObject *dict = NULL; bool newline = TRUE; if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ob", kwlist, &text, &dict, &newline)) { PyErr_Format(PyExc_TypeError, "Invalid arguments supplied to Mudsock.send"); return NULL; } // is dict None? set it to NULL for expand_to_char if(dict == Py_None) dict = NULL; // make sure the dictionary is a dictionary if(!(dict == NULL || PyDict_Check(dict))) { PyErr_Format(PyExc_TypeError, "Mudsock.send expects second argument to be a dict object."); return NULL; } // make sure we exist if( (me = PySocket_AsSocket(self)) == NULL) { PyErr_Format(PyExc_TypeError, "Tried to send nonexistent socket."); return NULL; } if(dict != NULL) PyDict_SetItemString(dict, "me", self); // build our script environment BUFFER *buf = newBuffer(1); bufferCat(buf, text); // expand out our dynamic descriptions if we have a dictionary supplied if(dict != NULL) { PyObject *env = restricted_script_dict(); PyDict_Update(env, dict); // do the expansion expand_dynamic_descs_dict(buf, env, get_script_locale()); Py_XDECREF(env); } if(newline == TRUE) bufferCat(buf, "\r\n"); text_to_buffer(me, bufferString(buf)); // garbage collection deleteBuffer(buf); return Py_BuildValue(""); } PyObject *PySocket_close(PySocket *self) { SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) return NULL; close_socket(sock, FALSE); return Py_BuildValue("i", 1); } PyObject *PySocket_edit_text(PyObject *self, PyObject *args) { PyObject *on_complete = NULL; char *text = NULL; char *type = NULL; SOCKET_DATA *sock = NULL; EDITOR *editor = text_editor; if(!PyArg_ParseTuple(args, "sO|s", &text, &on_complete, &type)) { PyErr_Format(PyExc_TypeError, "invalid arguments to sock.edit_text"); return NULL; } if(!PySocket_Check(self)) { PyErr_Format(PyExc_TypeError, "Owner of edit_text not a socket"); return NULL; } else if( (sock = PySocket_AsSocket(self)) == NULL) { PyErr_Format(PyExc_TypeError, "sock.edit_text called on non-existent socket."); return NULL; } if(type != NULL && compares(type, "script")) editor = script_editor; // begin the editor socketStartPyEditorFunc(sock, editor, text, on_complete); return Py_BuildValue("O", Py_None); } //***************************************************************************** // methods for the Socket class //***************************************************************************** // // returns the specified piece of auxiliary data from the socket // if it is a piece of python auxiliary data. PyObject *PySocket_get_auxiliary(PySocket *self, PyObject *args) { char *keyword = NULL; if(!PyArg_ParseTuple(args, "s", &keyword)) { PyErr_Format(PyExc_TypeError, "getAuxiliary() must be supplied with the name that the " "auxiliary data was installed under!"); return NULL; } // make sure we exist SOCKET_DATA *sock = PySocket_AsSocket((PyObject *)self); if(sock == NULL) { PyErr_Format(PyExc_StandardError, "Tried to get auxiliary data for a nonexistant socket."); return NULL; } // make sure the auxiliary data exists if(!pyAuxiliaryDataExists(keyword)) { PyErr_Format(PyExc_StandardError, "No auxiliary data named '%s' exists!", keyword); return NULL; } PyObject *data = socketGetAuxiliaryData(sock, keyword); if(data == NULL) data = Py_None; PyObject *retval = Py_BuildValue("O", data); // Py_DECREF(data); return retval; } //***************************************************************************** // structures to define our methods and classes //***************************************************************************** PyTypeObject PySocket_Type = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "mudsock.Mudsock", /*tp_name*/ sizeof(PySocket), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)PySocket_dealloc,/*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)PySocket_compare, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "Python Socket object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ 0, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)PySocket_init, /* tp_init */ 0, /* tp_alloc */ PySocket_new, /* tp_new */ }; //***************************************************************************** // methods in the mudsock module //***************************************************************************** PyObject *PySocket_all_sockets(PyObject *self) { PyObject *list = PyList_New(0); LIST_ITERATOR *sock_i = newListIterator(socket_list); SOCKET_DATA *sock = NULL; ITERATE_LIST(sock, sock_i) { PyList_Append(list, socketGetPyFormBorrowed(sock)); } deleteListIterator(sock_i); PyObject *retval = Py_BuildValue("O", list); Py_DECREF(list); return retval; } PyMethodDef socket_module_methods[] = { { "socket_list", (PyCFunction)PySocket_all_sockets, METH_NOARGS, "socket_list()\n\n" "Returns a list of all sockets currently connected." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //***************************************************************************** // implementation of pyexit.h //***************************************************************************** void PySocket_addGetSetter(const char *name, void *g, void *s,const char *doc){ // make sure our list of get/setters is created if(pysocket_getsetters == NULL) pysocket_getsetters = newList(); // make the GetSetter def PyGetSetDef *def = calloc(1, sizeof(PyGetSetDef)); def->name = strdup(name); def->get = (getter)g; def->set = (setter)s; def->doc = (doc ? strdup(doc) : NULL); def->closure = NULL; listPut(pysocket_getsetters, def); } void PySocket_addMethod(const char *name, void *f, int flags, const char *doc){ // make sure our list of methods is created if(pysocket_methods == NULL) pysocket_methods = newList(); // make the Method def PyMethodDef *def = calloc(1, sizeof(PyMethodDef)); def->ml_name = strdup(name); def->ml_meth = (PyCFunction)f; def->ml_flags = flags; def->ml_doc = (doc ? strdup(doc) : NULL); listPut(pysocket_methods, def); } // initialize sockets for use. This must be called AFTER PyMODINIT_FUNC init_PySocket(void) { PyObject* module = NULL; // add all of the basic getsetters PySocket_addGetSetter("uid", PySocket_getuid, NULL, "The socket's uid. Immutable."); PySocket_addGetSetter("account", PySocket_getaccount, NULL, "The account currently attached to the socket, or None. Immutable.\n" "see mudsys.attach_account_socket for connecting sockets and accounts."); PySocket_addGetSetter("character", PySocket_getchar, NULL, "The character currently attached to the socket, on None. Immutable.\n" "see mudsys.attach_char_socket for connecting characters to account."); PySocket_addGetSetter("char", PySocket_getchar, NULL, "Alias for mudsock.Mudsock.character"); PySocket_addGetSetter("ch", PySocket_getchar, NULL, "Alias for mudsock.Mudsock.character."); PySocket_addGetSetter("has_input", PySocket_getcmdread, NULL, "True or False if the socket has any input pending. Immutable."); PySocket_addGetSetter("outbound_text", PySocket_get_outbound_text, PySocket_set_outbound_text, "The socket's outbound text."); PySocket_addGetSetter("can_use", PySocket_get_can_use, NULL, "True or False if the socket is ready for use. Socket becomes available\n" "after its dns addresss resolves. Immutable."); PySocket_addGetSetter("state", PySocket_getstate, NULL, "The state that the socket is in. Immutable. For more on states see\n" "mudsock.Mudsock.push_ih"); PySocket_addGetSetter("idle_time", PySocket_getidletime, NULL, "How long (in seconds) the socket's input handler has been idle for. Immutable."); PySocket_addGetSetter("hostname", PySocket_gethostname, NULL, "The dns address that the socket is connected from. Immutable."); PySocket_addGetSetter("cols", PySocket_getcols, PySocket_setcols, "The width of the client's output in columns."); PySocket_addGetSetter("rows", PySocket_getrows, PySocket_setrows, "The height of the client's output in rows."); // add all of the basic methods PySocket_addMethod("getAuxiliary", PySocket_get_auxiliary, METH_VARARGS, "getAuxiliary(name)\n\n" "Returns socket's auxiliary data of the specified name."); PySocket_addMethod("aux", PySocket_get_auxiliary, METH_VARARGS, "Alias for mudsock.Mudsock.getAuxiliary"); PySocket_addMethod("send", PySocket_send, METH_KEYWORDS, "send(mssg, dict = None, newline = True)\n" "\n" "Sends message to the socket. Messages can have scripts embedded in\n" "them, using [ and ]. If so, a variable dictionary must be provided. By\n" "default, 'me' references the socket being sent the message."); PySocket_addMethod("trans_raw", PySocket_send_rraw, METH_VARARGS, "trans_raw(mssg)\n\n" "Sends text to the socket. No appended newline, and no checking for the prompt."); PySocket_addMethod("send_raw", PySocket_send_raw, METH_VARARGS, "send_raw(mssg)\n\n" "Sends text to the socket. No appended newline."); PySocket_addMethod("pop_ih", PySocket_pop_ih, METH_NOARGS, "pop_ih()\n\n" "Pops the socket's current input handler from its input handler stack."); PySocket_addMethod("push_ih", PySocket_push_ih, METH_VARARGS, "push_ih(handler_func, prompt_func, state=None)\n\n" "Pushes a new input handler and prompt pair onto the socket's input\n" "handler stack. Optionally, a (String) state value can be supplied.\n" "Input handlers take two arguments: the socket and a string command.\n" "Prompts take one argument: the socket. They should send the relevant\n" "text for the prompt to the socket."); PySocket_addMethod("replace_ih", PySocket_replace_ih, METH_VARARGS, "repalce_ih(handler_func, prompt_func, state=None)\n\n" "Calls pop_ih, followed by push_ih."); PySocket_addMethod("close", PySocket_close, METH_VARARGS, "close()\n\n" "Closes the socket's connection."); PySocket_addMethod("bust_prompt", PySocket_bust_prompt, METH_NOARGS, "bust_prompt()\n\n" "Busts the socket's prompt so it will be displayed next pulse."); PySocket_addMethod("edit_text", PySocket_edit_text, METH_VARARGS, "edit_text(dflt_value, on_complete, mode='text')\n\n" "Enter the text editor, and set its default value. When the text editor\n" "is edited, call on_complete. This function should take two arguments:\n" "the socket doing the editing, and the output of the editor. Mode can\n" "be 'text' or 'script'."); // add in all the getsetters and methods makePyType(&PySocket_Type, pysocket_getsetters, pysocket_methods); deleteListWith(pysocket_getsetters, free); pysocket_getsetters = NULL; deleteListWith(pysocket_methods, free); pysocket_methods = NULL; // make sure the socket class is ready to be made if (PyType_Ready(&PySocket_Type) < 0) return; // initialize the module module = Py_InitModule3("mudsock", socket_module_methods, "Contains the Python wrapper for sockets, and utilities for listing\n" "currently connected sockets."); // make sure the module parsed OK if (module == NULL) return; // add the Socket class to the socket module PyTypeObject *type = &PySocket_Type; PyModule_AddObject(module, "Mudsock", (PyObject *)type); Py_INCREF(&PySocket_Type); } int PySocket_Check(PyObject *value) { return PyObject_TypeCheck(value, &PySocket_Type); } int PySocket_AsUid(PyObject *sock) { return ((PySocket *)sock)->uid; } SOCKET_DATA *PySocket_AsSocket(PyObject *ch) { return propertyTableGet(sock_table, PySocket_AsUid(ch)); } PyObject * newPySocket(SOCKET_DATA *sock) { PySocket *pysock = (PySocket *)PySocket_new(&PySocket_Type, NULL, NULL); pysock->uid = socketGetUID(sock); return (PyObject *)pysock; }