source: TI05-delivery/trunk/src/python_ext/bbftpd.c @ 1444

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/src/python_ext/bbftpd.c@1444
Revision 1444, 11.6 KB checked in by spascoe, 13 years ago (diff)

BROKEN!

I'm in the middle of implementing a logging callback in the server.

Line 
1/**
2 * NDG python embedded bbftp daemon module.
3 *
4 * @author Stephen Pascoe
5 *
6 * Copyright (C) 2006 CCLRC & NERC
7 *
8 * This software may be distributed under the terms of the Q Public Licence, version 1.0 or later.
9 *
10 */
11
12
13#include <Python.h>
14#include <stdlib.h>
15#include <stdarg.h>
16#include <ndg.h>
17#include "util.h"
18#include <structures.h>
19#include <bbftpd_private_log.h>
20
21extern char **environ;
22/*
23 * variables and prototypes from bbftpd source
24 */
25
26extern int be_daemon;
27extern int newcontrolport;
28
29int bbftpd_main(int argc, char **argv, char **envp);
30int ndg_message_send(char *buffer, int buffertosendlength, char *logmessage) ;
31int ndg_message_recv(char **buffer, int *length, char *logmessage) ;
32
33/*
34 * Static variables.
35 */
36static PyObject *authHandler = NULL;
37static PyObject *authzHandler = NULL;
38static PyObject *serverLogger = NULL;
39
40
41/**
42 * Get a logger object from the logging module.
43 *
44 * @param loggerName the name of the logger passed to logging.getLogger()
45 * @return a new reference to the logger object
46 */
47static PyObject *getLogger(char *loggerName) {
48  PyObject *logging, *logger;
49
50  if ((logging = PyImport_ImportModule("logging")) == NULL) {
51    return NULL;
52  }
53  if ((logger = PyObject_CallMethod(logging, "getLogger", "s", loggerName)) == NULL) {
54    return NULL;
55  }
56
57  return logger;
58}
59
60
61/*---------------------------------------------------------------------------------------------------
62 * The following functions interact with bbftpd_private_user.  They are placed here to separate
63 * python and bbftp APIs.
64 *
65 */
66
67
68
69/**
70 * Make a callback bbftpd->python to handle authentication.
71 *
72 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE
73 *     for storing an error message when returning -1.
74 * @return 0 on success, -1 on failure
75 */
76int ndg_auth(char *logmessage) {
77
78  if (authzHandler != NULL) {
79    sprintf(logmessage, "ndg_auth: authzHandler already present");
80    return -1;
81  }
82 
83  if (authHandler == NULL) {
84    sprintf(logmessage, "ndg_auth: no authHandler set");
85    return -1;
86  }
87
88  /** @todo exceptions of type delivery.server.AuthenticationFailure should be logged as
89   *      failure rather than error.
90   */
91  if ((authzHandler = PyObject_CallMethod(authHandler, "authenticate", NULL)) == NULL) {
92    ndg_pyerr_to_logmessage(logmessage, "ndg_auth");
93    return -1;
94  }
95
96  /* Authorisation is considered failed if the return value isn't True.  */
97  if (!PyObject_IsTrue(authzHandler)) {
98    sprintf(logmessage, "ndg_auth: python authentication failed");
99    return -1;
100  }
101
102  return 0;
103}
104
105/**
106 * retrieve the username from authzHandler.
107 *
108 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE
109 *     for storing an error message when returning NULL.
110 * @return pointer to the username or NULL on failure
111 */
112char *ndg_getusername(char *logmessage) {
113  char *username;
114  PyObject *str_obj;
115
116  if (authzHandler == NULL) {
117    sprintf(logmessage, "bbftpd_private_auth_getusername: no authzHandler set");
118    return NULL;
119  }
120
121  if ((str_obj = PyObject_GetAttrString(authzHandler, "username")) == NULL) {
122    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername");
123    return NULL;
124  }
125
126  if ((username = PyString_AsString(str_obj)) == NULL) {
127    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername");
128    Py_DECREF(str_obj);
129    return NULL;
130  }
131
132  return username;
133}
134
135
136/**
137 * Make a callback to do authorisation of a control command.
138 *
139 * @see bbftpd_private_authz_control()
140 */
141int ndg_authz_control(int msgcode, int transferoption, char *path, char *logmessage) {
142  PyObject *ret_obj;
143
144  if (authzHandler == NULL) {
145    sprintf(logmessage, "ndg_authz_control: no authzHandler set");
146    return -1;
147  }
148 
149  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzControl", "iis", msgcode, transferoption, path)) == NULL) {
150    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control");
151    return -1;
152  }
153 
154  /* Authorisation fails if ret_obj is false. */
155  if (!PyObject_IsTrue(ret_obj)) {
156    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control");
157    Py_DECREF(ret_obj);
158    return -1;
159  }
160
161  return 0;
162}
163
164/**
165 * Make a callback to do authorisation of a retr command.
166 *
167 * @see bbftpd_private_authz_retr()
168 */
169int ndg_authz_retr(char *path, char *logmessage) {
170  PyObject *ret_obj;
171
172  if (authzHandler == NULL) {
173    sprintf(logmessage, "ndg_authz_retr: no authzHandler set");
174    return -1;
175  }
176 
177  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzRetr", "s", path)) == NULL) {
178    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr");
179    return -1;
180  }
181 
182  /* Authorisation fails if ret_obj is false. */
183  if (!PyObject_IsTrue(ret_obj)) {
184    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr");
185    Py_DECREF(ret_obj);
186    return -1;
187  }
188
189  return 0;
190}
191
192
193/**
194 * Make a callback to do authorisation of a store command.
195 *
196 * @see bbftpd_private_authz_store()
197 */
198int ndg_authz_store(char *path, char *logmessage) {
199  PyObject *ret_obj;
200
201  if (authzHandler == NULL) {
202    sprintf(logmessage, "ndg_authz_store: no authzHandler set");
203    return -1;
204  }
205 
206  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzStore", "s", path)) == NULL) {
207    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store");
208    return -1;
209  }
210 
211  /* Authorisation fails if ret_obj is false. */
212  if (!PyObject_IsTrue(ret_obj)) {
213    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store");
214    Py_DECREF(ret_obj);
215    return -1;
216  }
217
218  return 0;
219}
220 
221/**
222 * Make a callback to log a message to the ndg.delivery.server logger.
223 *
224 * @param priority syslog-style priority
225 * @param format the message with printf-style format specifiers
226 * @param vargs argument list for inclusion in the message.
227 */
228void ndg_log(int priority, const char *format, va_list vargs) {
229  PyObject *message;
230  char *priority_method;
231
232  /* Just in case this is somehow called outsite pybbftpd_run() */
233  if (serverLogger == NULL) return;
234 
235  if ((message = PyString_FromFormatV(format, vargs)) == NULL) {
236    if ((message = PyString_FromString("ndg_log: PyString_FromFormatV error")) == NULL) {
237      /* Desperate measures! */
238      abort();
239    }
240  }
241
242  switch (priority) {
243  case LOG_DEBUG:
244    priority_method = "debug"; break;
245  case LOG_INFO:
246    priority_method = "info"; break;
247  case LOG_NOTICE:
248    priority_method = "info"; break;
249  case LOG_WARNING:
250    priority_method = "warning"; break;
251  case LOG_ERR:
252    priority_method = "error"; break;
253  default:
254    priority_method = "critical"; break;
255  }
256
257  if ((PyObject_CallMethod(serverLogger, priority_method, "O", message)) == NULL) {
258    /*!TODO: Work out how better to recover from this error */
259    abort();
260  }
261
262  Py_DECREF(message);
263  return;
264}
265     
266/*-------------------------------------------------------------------------------------------------
267 * Functions exported to python
268 *
269 */
270
271/* log message to bbftpd logger */
272static PyObject *pybbftpd_log(PyObject *self, PyObject *args) {
273  int priority;
274  char *message;
275
276  if (!PyArg_ParseTuple(args, "is", &priority, &message)) {
277    return NULL;
278  }
279
280  bbftpd_log(priority, message);
281
282  Py_RETURN_NONE;
283}
284
285/* send(string) */
286static PyObject *pybbftpd_send(PyObject *self, PyObject *args) {
287  char *buffer, logmessage[1024];
288  int len;
289
290  if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) {
291    return NULL;
292  }
293
294  if (authHandler == NULL) {
295    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback");
296    return NULL;
297  }
298
299  if (ndg_message_send(buffer, len, logmessage) == -1) {
300    PyErr_SetString(PyExc_IOError, logmessage);
301    return NULL;
302  }
303
304  Py_RETURN_NONE;
305}
306
307/* recv(length) -> string */
308static PyObject *pybbftpd_recv(PyObject *self, PyObject *args) {
309  char *buffer, logmessage[1024];
310  int len;
311  PyObject *ret;
312
313  if (!PyArg_ParseTuple(args, "")) {
314    return NULL;
315  }
316
317  if (authHandler == NULL) {
318    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback");
319    return NULL;
320  }
321
322  if (ndg_message_recv(&buffer, &len, logmessage) == -1) {
323    PyErr_SetString(PyExc_IOError, logmessage);
324    free(buffer);
325    return NULL;
326  }
327
328  ret = Py_BuildValue("s#", buffer, len);
329  free(buffer);
330
331  return ret;
332}
333
334/**
335 * Main entry point for the python module.
336 */
337static PyObject *pybbftpd_run(PyObject *self, PyObject *args) {
338  int argc, i;
339  char **argv, **arg_p, *p;
340  int pid;
341  char preargs[2][20] = { "bbftpd_embedded", "-b" };
342  PyObject *daemon_args, *item;
343
344  if (!PyArg_ParseTuple(args, "OO", &authHandler, &daemon_args)) {
345    return NULL;
346  }
347  Py_INCREF(authHandler);
348  Py_INCREF(daemon_args);
349
350  /*
351   * Convert arguments into a standard argv sequence.
352   */
353  argc = PySequence_Size(daemon_args);
354  if ((argv = (char**)malloc((argc + 2)*sizeof(char*))) == NULL) {
355    PyErr_SetString(PyExc_MemoryError, "malloc failed");
356    Py_DECREF(daemon_args);
357    return NULL;
358  }
359
360  // Add fixed arguments to daemon's argv
361  arg_p = argv;
362  *arg_p = preargs[0]; arg_p++;
363  *arg_p = preargs[1]; arg_p++;
364
365  for (i=0; i<argc; i++) {
366    if ((item = PySequence_GetItem(daemon_args, i)) == NULL) {
367      free(argv);
368      Py_DECREF(authHandler);
369      Py_DECREF(daemon_args);
370      return NULL;
371    }
372    if ((p = PyString_AsString(item)) == NULL) {
373      free(argv);
374      Py_DECREF(item);
375      Py_DECREF(authHandler);
376      Py_DECREF(daemon_args);
377      return NULL;
378    }
379    if ((*arg_p = (char *)malloc(strlen(p))) == NULL) {
380      Py_DECREF(item);
381      Py_DECREF(authHandler);
382      Py_DECREF(daemon_args);
383      PyErr_SetString(PyExc_MemoryError, "malloc failed");
384      return NULL;
385    }
386    strcpy(*arg_p, p);
387
388    arg_p++;
389    Py_DECREF(item);
390  }
391
392  /* Initialise logger */
393  if (serverLogger != NULL) {
394    Py_DECREF(serverLogger);
395  }
396  if ((serverLogger = getLogger("ndg.delivery.server")) == NULL) {
397    return NULL;
398  }
399
400  /*
401   * We must reset some global variables so that bbftpd_run can be executed multiple times.
402   */
403  be_daemon = 0;
404
405  pid = bbftpd_main(argc+2, argv, environ);
406
407  free(argv);
408  Py_DECREF(authHandler); authHandler = NULL;
409  Py_DECREF(daemon_args);
410
411  return Py_BuildValue("i", pid);
412}
413
414static PyMethodDef BbftpdMethods[] = {
415  {
416    "run", pybbftpd_run, METH_VARARGS, 
417    "Execute the bbftpd server.\n"
418    "\n"
419    "Forks an embedded bbftpd process as if it were executed\n"
420    "with the command line $ bbftpd -b <args>."
421    "\n"
422    "@param args: command line arguments to pass to the server\n"
423    "@return: the PID of the server process\n"
424  },
425  {
426    "send", pybbftpd_send, METH_VARARGS,
427    "Send an authentication message to the client.\n"
428    "\n"
429    "Equivilent to bbftpd_private_send() C call.\n"
430    "\n"
431    "@param buffer: A string containing the message\n"
432    "@raise IOError: if bbftpd_private_send() call fails\n"
433  },
434  {
435    "recv", pybbftpd_recv, METH_VARARGS,
436    "Receive an authentication message from the client.\n"
437    "\n"
438    "Equivilent to bbftpd_private_recv() C call.\n"
439    "\n"
440    "@param len: The length of message to expect\n"
441    "@return: A string containing the message\n"
442    "@raise IOError: if bbftpd_private_recv() call fails\n"
443  },
444  {
445    "log", pybbftpd_log, METH_VARARGS,
446    "Send a message to the bbftpd logger.\n"
447    "\n"
448    "@param priority: as syslog priority\n"
449    "@param message: A string containing the message to send\n"
450  },
451  {NULL, NULL, 0, NULL}
452};
453
454
455#define ADDCONST(m,x) PyModule_AddIntConstant(m, #x, x)
456
457PyMODINIT_FUNC initbbftpd(void) {
458  PyObject *mod;
459
460  mod = Py_InitModule("bbftpd", BbftpdMethods);
461
462  ADDCONST(mod, MSG_CHDIR_V2);
463  ADDCONST(mod, MSG_CHDIR_V2);
464  ADDCONST(mod, MSG_LIST_V2);
465  ADDCONST(mod, MSG_MKDIR_V2);
466  ADDCONST(mod, MSG_RM);
467  ADDCONST(mod, MSG_STAT);
468  ADDCONST(mod, MSG_DF);
469 
470  ADDCONST(mod, TROPT_TMP);
471  ADDCONST(mod, TROPT_ACC);
472  ADDCONST(mod, TROPT_MODE);
473  ADDCONST(mod, TROPT_DIR);
474  ADDCONST(mod, TROPT_GZIP);
475  ADDCONST(mod, TROPT_RFIO);
476  ADDCONST(mod, TROPT_RFIO_O);
477  ADDCONST(mod, TROPT_QBSS);
478}
479
Note: See TracBrowser for help on using the repository browser.