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

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

Defined constants used by bbftp in delivery.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 <ndg.h>
15
16
17extern char **environ;
18/*
19 * variables and prototypes from bbftpd source
20 */
21
22extern int be_daemon;
23extern int newcontrolport;
24
25int bbftpd_main(int argc, char **argv, char **envp);
26int ndg_message_send(char *buffer, int buffertosendlength, char *logmessage) ;
27int ndg_message_recv(char **buffer, int *length, char *logmessage) ;
28
29/*
30 * Static variables.
31 */
32static PyObject *authHandler = NULL;
33static PyObject *authzHandler = NULL;
34
35static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix);
36
37/*---------------------------------------------------------------------------------------------------
38 * The following functions interact with bbftpd_private_user.  They are placed here to separate
39 * python and bbftp APIs.
40 *
41 */
42
43/**
44 * Copy the python exception message to a bbftp logmessage.
45 *
46 * If the python error indicator is set it will be cleared.
47 * If the python error indicator is not set the empty string will be copied to \a logmessage.
48 * The exception message is truncated if it exceeds \c NDG_MAX_LOGMESSAGE.
49 *
50 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE
51 *     for storing an error message when returning -1.
52 * @param prefix a string to be prefixed to the logmessage.
53 */
54static void ndg_pyerr_to_logmessage(char *logmessage, char *prefix) {
55  PyObject *ptype, *pvalue, *ptraceback, *obj;
56  char *exname, *exvalue;
57
58  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
59 
60  if ((obj = PyObject_GetAttrString(ptype, "__name__")) == NULL) goto error;
61  exname = PyString_AsString(obj); Py_DECREF(obj);
62  if (exname == NULL) goto error;
63
64  if ((obj = PyObject_Str(pvalue)) == NULL) goto error;
65  exvalue = PyString_AsString(obj); Py_DECREF(obj);
66  if (exvalue == NULL) goto error;
67
68  snprintf(logmessage, NDG_MAX_LOGMESSAGE, "%s: %s: %s", prefix, exname, exvalue);
69  return;
70 
71 error:
72  sprintf("ndg_pyerr_to_logmessage: internal python error", logmessage);
73  return;
74}
75
76/**
77 * Make a callback bbftpd->python to handle authentication.
78 *
79 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE
80 *     for storing an error message when returning -1.
81 * @return 0 on success, -1 on failure
82 */
83int ndg_auth(char *logmessage) {
84
85  if (authzHandler != NULL) {
86    sprintf(logmessage, "ndg_auth: authzHandler already present");
87    return -1;
88  }
89 
90  if (authHandler == NULL) {
91    sprintf(logmessage, "ndg_auth: no authHandler set");
92    return -1;
93  }
94
95  /** @todo exceptions of type delivery.server.AuthenticationFailure should be logged as
96   *      failure rather than error.
97   */
98  if ((authzHandler = PyObject_CallMethod(authHandler, "authenticate", "")) == NULL) {
99    ndg_pyerr_to_logmessage(logmessage, "ndg_auth");
100    return -1;
101  }
102
103  /* Authorisation is considered failed if the return value isn't True.  */
104  if (!PyObject_IsTrue(authzHandler)) {
105    sprintf(logmessage, "ndg_auth: python authentication failed");
106    return -1;
107  }
108
109  return 0;
110}
111
112/**
113 * retrieve the username from authzHandler.
114 *
115 * @param logmessage a pointer to a buffer of length \c NDG_MAX_LOGMESSAGE
116 *     for storing an error message when returning NULL.
117 * @return pointer to the username or NULL on failure
118 */
119char *ndg_getusername(char *logmessage) {
120  char *username;
121  PyObject *str_obj;
122
123  if (authzHandler == NULL) {
124    sprintf(logmessage, "bbftpd_private_auth_getusername: no authzHandler set");
125    return NULL;
126  }
127
128  if ((str_obj = PyObject_GetAttrString(authzHandler, "username")) == NULL) {
129    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername");
130    return NULL;
131  }
132
133  if ((username = PyString_AsString(str_obj)) == NULL) {
134    ndg_pyerr_to_logmessage(logmessage, "ndg_getusername");
135    Py_DECREF(str_obj);
136    return NULL;
137  }
138
139  return username;
140}
141
142
143/**
144 * Make a callback to do authorisation of a control command.
145 *
146 * @see bbftpd_private_authz_control()
147 */
148int ndg_authz_control(int msgcode, int transferoption, char *path, char *logmessage) {
149  PyObject *ret_obj;
150
151  if (authzHandler == NULL) {
152    sprintf(logmessage, "ndg_authz_control: no authzHandler set");
153    return -1;
154  }
155 
156  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzControl", "iis", msgcode, transferoption, path)) == NULL) {
157    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control");
158    return -1;
159  }
160 
161  /* Authorisation fails if ret_obj is false. */
162  if (!PyObject_IsTrue(ret_obj)) {
163    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_control");
164    Py_DECREF(ret_obj);
165    return -1;
166  }
167
168  return 0;
169}
170
171/**
172 * Make a callback to do authorisation of a retr command.
173 *
174 * @see bbftpd_private_authz_retr()
175 */
176int ndg_authz_retr(char *path, char *logmessage) {
177  PyObject *ret_obj;
178
179  if (authzHandler == NULL) {
180    sprintf(logmessage, "ndg_authz_retr: no authzHandler set");
181    return -1;
182  }
183 
184  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzRetr", "s", path)) == NULL) {
185    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr");
186    return -1;
187  }
188 
189  /* Authorisation fails if ret_obj is false. */
190  if (!PyObject_IsTrue(ret_obj)) {
191    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_retr");
192    Py_DECREF(ret_obj);
193    return -1;
194  }
195
196  return 0;
197}
198
199
200/**
201 * Make a callback to do authorisation of a store command.
202 *
203 * @see bbftpd_private_authz_store()
204 */
205int ndg_authz_store(char *path, char *logmessage) {
206  PyObject *ret_obj;
207
208  if (authzHandler == NULL) {
209    sprintf(logmessage, "ndg_authz_store: no authzHandler set");
210    return -1;
211  }
212 
213  if ((ret_obj = PyObject_CallMethod(authzHandler, "authzStore", "s", path)) == NULL) {
214    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store");
215    return -1;
216  }
217 
218  /* Authorisation fails if ret_obj is false. */
219  if (!PyObject_IsTrue(ret_obj)) {
220    ndg_pyerr_to_logmessage(logmessage, "ndg_authz_store");
221    Py_DECREF(ret_obj);
222    return -1;
223  }
224
225  return 0;
226}
227 
228
229     
230/*-------------------------------------------------------------------------------------------------
231 * Functions exported to python
232 *
233 */
234
235/* send(string) */
236static PyObject *bbftpd_send(PyObject *self, PyObject *args) {
237  char *buffer, logmessage[1024];
238  int len;
239
240  if (!PyArg_ParseTuple(args, "s#", &buffer, &len)) {
241    return NULL;
242  }
243
244  if (authHandler == NULL) {
245    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback");
246    return NULL;
247  }
248
249  if (ndg_message_send(buffer, len, logmessage) == -1) {
250    PyErr_SetString(PyExc_IOError, logmessage);
251    return NULL;
252  }
253
254  Py_RETURN_NONE;
255}
256
257/* recv(length) -> string */
258static PyObject *bbftpd_recv(PyObject *self, PyObject *args) {
259  char *buffer, logmessage[1024];
260  int len;
261  PyObject *ret;
262
263  if (!PyArg_ParseTuple(args, "")) {
264    return NULL;
265  }
266
267  if (authHandler == NULL) {
268    PyErr_SetString(PyExc_RuntimeError, "Must be called within a bbftpd callback");
269    return NULL;
270  }
271
272  if (ndg_message_recv(&buffer, &len, logmessage) == -1) {
273    PyErr_SetString(PyExc_IOError, logmessage);
274    free(buffer);
275    return NULL;
276  }
277
278  ret = Py_BuildValue("s#", buffer, len);
279  free(buffer);
280  return ret;
281}
282
283
284
285/**
286 * Main entry point for the python module.
287 */
288static PyObject *bbftpd_run(PyObject *self, PyObject *args) {
289  int argc, i;
290  char **argv, **arg_p;
291  int pid;
292  char preargs[2][20] = { "bbftpd_embedded", "-b" };
293  PyObject *daemon_args, *item;
294
295  if (!PyArg_ParseTuple(args, "OO", &authHandler, &daemon_args)) {
296    return NULL;
297  }
298  Py_INCREF(authHandler);
299  Py_INCREF(daemon_args);
300
301  /*
302   * Convert arguments into a standard argv sequence.
303   */
304  argc = PySequence_Size(daemon_args);
305  if ((argv = (char**)malloc((argc + 2)*sizeof(char*))) == NULL) {
306    PyErr_SetString(PyExc_MemoryError, "malloc failed");
307    Py_DECREF(daemon_args);
308    return NULL;
309  }
310
311  // Add fixed arguments to daemon's argv
312  arg_p = argv;
313  *arg_p = preargs[0]; arg_p++;
314  *arg_p = preargs[1]; arg_p++;
315
316  for (i=0; i<argc; i++) {
317    if ((item = PySequence_GetItem(daemon_args, i)) == NULL) {
318      free(argv);
319      Py_DECREF(authHandler);
320      Py_DECREF(daemon_args);
321      return NULL;
322    }
323    if ((*arg_p = PyString_AsString(item)) == NULL) {
324      free(argv);
325      Py_DECREF(item);
326      Py_DECREF(authHandler);
327      Py_DECREF(daemon_args);
328      return NULL;
329    }
330    arg_p++;
331    Py_DECREF(item);
332  }
333
334  /*
335   * We must reset some global variables so that bbftpd_run can be executed multiple times.
336   */
337  be_daemon = 0;
338
339  pid = bbftpd_main(argc+2, argv, environ);
340
341  free(argv);
342  Py_DECREF(authHandler); authHandler = NULL;
343  Py_DECREF(daemon_args);
344
345  return Py_BuildValue("i", pid);
346}
347
348static PyMethodDef BbftpdMethods[] = {
349  {
350    "run", bbftpd_run, METH_VARARGS, 
351    "Execute the bbftpd server.\n"
352    "\n"
353    "Forks an embedded bbftpd process as if it were executed\n"
354    "with the command line $ bbftpd -b <args>."
355    "\n"
356    "@param args: command line arguments to pass to the server\n"
357    "@return: the PID of the server process\n"
358  },
359  {
360    "send", bbftpd_send, METH_VARARGS,
361    "Send an authentication message to the client.\n"
362    "\n"
363    "Equivilent to bbftpd_private_send() C call.\n"
364    "\n"
365    "@param buffer: A string containing the message\n"
366    "@raise IOError: if bbftpd_private_send() call fails\n"
367  },
368  {
369    "recv", bbftpd_recv, METH_VARARGS,
370    "Receive an authentication message from the client.\n"
371    "\n"
372    "Equivilent to bbftpd_private_recv() C call.\n"
373    "\n"
374    "@param len: The length of message to expect\n"
375    "@return: A string containing the message\n"
376    "@raise IOError: if bbftpd_private_recv() call fails\n"
377  },
378  {NULL, NULL, 0, NULL}
379};
380
381
382#define ADDCONST(m,x) PyModule_AddIntConstant(m, #x, x)
383
384PyMODINIT_FUNC initbbftpd(void) {
385  PyObject *mod;
386
387  mod = Py_InitModule("bbftpd", BbftpdMethods);
388
389  ADDCONST(mod, MSG_CHDIR_V2);
390  ADDCONST(mod, MSG_CHDIR_V2);
391  ADDCONST(mod, MSG_LIST_V2);
392  ADDCONST(mod, MSG_MKDIR_V2);
393  ADDCONST(mod, MSG_RM);
394  ADDCONST(mod, MSG_STAT);
395  ADDCONST(mod, MSG_DF);
396 
397  ADDCONST(mod, TROPT_TMP);
398  ADDCONST(mod, TROPT_ACC);
399  ADDCONST(mod, TROPT_MODE);
400  ADDCONST(mod, TROPT_DIR);
401  ADDCONST(mod, TROPT_GZIP);
402  ADDCONST(mod, TROPT_RFIO);
403  ADDCONST(mod, TROPT_RFIO_O);
404  ADDCONST(mod, TROPT_QBSS);
405 
406}
407
Note: See TracBrowser for help on using the repository browser.