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

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

Client-side python embedding is being implemented but doesn't pass the
test cases yet.

I've uncovered a bug (see #343) which needs fixing before the embedded client
can be fully debugged.

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