author | korovkin <korovkin> | 2006-07-06 16:28:08 (UTC) |
---|---|---|
committer | korovkin <korovkin> | 2006-07-06 16:28:08 (UTC) |
commit | 43cd66c08de4447998028179d20fd4817aaf16ca (patch) (unidiff) | |
tree | 87888aadc3398c3c633e332cda46d92bb0522d6a /core/obex/obexserver.cpp | |
parent | adcfc6f4afe184a9eb6fbf458616494dfe0dadda (diff) | |
download | opie-43cd66c08de4447998028179d20fd4817aaf16ca.zip opie-43cd66c08de4447998028179d20fd4817aaf16ca.tar.gz opie-43cd66c08de4447998028179d20fd4817aaf16ca.tar.bz2 |
Added OBEX Push functionality for Bluetooth.
- Added ObexBase - asic class for IR and BT Obex
- Added ObexServer - OBEX Push server.
-rw-r--r-- | core/obex/obexserver.cpp | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/core/obex/obexserver.cpp b/core/obex/obexserver.cpp new file mode 100644 index 0000000..95196de --- a/dev/null +++ b/core/obex/obexserver.cpp | |||
@@ -0,0 +1,514 @@ | |||
1 | /* | ||
2 | =. This file is part of the OPIE Project | ||
3 | .=l. Copyright (c) 2002 Maximilian Reiss <max.reiss@gmx.de> | ||
4 | .>+-= | ||
5 | _;:, .> :=|. This library is free software; you can | ||
6 | .> <, > . <= redistribute it and/or modify it under | ||
7 | :=1 )Y*s>-.-- : the terms of the GNU Library General Public | ||
8 | .="- .-=="i, .._ License as published by the Free Software | ||
9 | - . .-<_> .<> Foundation; version 2 of the License. | ||
10 | ._= =} : | ||
11 | .%+i> _;_. | ||
12 | .i_,=:_. -<s. This library is distributed in the hope that | ||
13 | + . -:. = it will be useful, but WITHOUT ANY WARRANTY; | ||
14 | : .. .:, . . . without even the implied warranty of | ||
15 | =_ + =;=| MERCHANTABILITY or FITNESS FOR A | ||
16 | _.=:. : :=>: PARTICULAR PURPOSE. See the GNU | ||
17 | ..}^=.= = ; Library General Public License for more | ||
18 | ++= -. . .: details. | ||
19 | : = ...= . :.=- | ||
20 | -. .:....=;==+<; You should have received a copy of the GNU | ||
21 | -_. . . )=. = Library General Public License along with | ||
22 | -- :-= this library; see the file COPYING.LIB. | ||
23 | If not, write to the Free Software Foundation, | ||
24 | Inc., 59 Temple Place - Suite 330, | ||
25 | Boston, MA 02111-1307, USA. | ||
26 | |||
27 | */ | ||
28 | /* | ||
29 | * The OBEX server class implementation | ||
30 | * Based on OBEX server from GPE (thanks, guys) | ||
31 | */ | ||
32 | |||
33 | #include "obexserver.h" | ||
34 | #include <unistd.h> | ||
35 | #include <opie2/odebug.h> | ||
36 | #include <sys/types.h> | ||
37 | #include <sys/wait.h> | ||
38 | #include <errno.h> | ||
39 | #include <stdlib.h> | ||
40 | #include <stdio.h> | ||
41 | #include <fcntl.h> | ||
42 | #include <qapplication.h> | ||
43 | #include <opie2/oprocctrl.h> | ||
44 | #include <qstring.h> | ||
45 | #include <qfile.h> | ||
46 | |||
47 | using namespace Opie::Core; | ||
48 | using namespace Opie::Core::Internal; | ||
49 | using namespace OpieObex; | ||
50 | |||
51 | ObexServer::ObexServer() : | ||
52 | OProcess(tr("ObexServer"), 0, "ObexServer") | ||
53 | { | ||
54 | m_obex = NULL; | ||
55 | } | ||
56 | |||
57 | ObexServer::~ObexServer() | ||
58 | { | ||
59 | stop(); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Function handles the file received | ||
64 | * @param name the file name | ||
65 | * @param data the file data | ||
66 | * @param data_len the data length | ||
67 | * @return 0 on success -1 on error | ||
68 | */ | ||
69 | static int file_received(uint8_t* name, const uint8_t* data, size_t data_len) | ||
70 | { | ||
71 | QString path("/tmp/"); | ||
72 | path += (char*)name; | ||
73 | QFile out(path); | ||
74 | int err = 0; | ||
75 | |||
76 | if (!out.open(IO_Raw | IO_ReadWrite | IO_Truncate)) { | ||
77 | printf("File %s open error %d\n", (const char*)path, errno); | ||
78 | err = -1; | ||
79 | goto out; | ||
80 | } | ||
81 | if (out.writeBlock((const char*)data, data_len) < 0) { | ||
82 | printf("File %s write error %d\n", (const char*)path, errno); | ||
83 | err = -1; | ||
84 | goto out; | ||
85 | } | ||
86 | out: | ||
87 | out.close(); | ||
88 | if (err == 0) { | ||
89 | printf("Wrote %s (%d bytes)\n", (const char*)path, data_len); | ||
90 | fflush(stdout); | ||
91 | } | ||
92 | return err; | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Function handles the situation when the PUT request has been done | ||
97 | * @param handle OBEX connection handle | ||
98 | * @param object OBEX object itself | ||
99 | */ | ||
100 | static int put_done(obex_t* handle, obex_object_t* object) | ||
101 | { | ||
102 | obex_headerdata_t hv; //Received file header | ||
103 | uint8_t hi; //Type of the request | ||
104 | uint32_t hlen; //File (file name) length | ||
105 | int err = 0; | ||
106 | |||
107 | const uint8_t *body = NULL; | ||
108 | int body_len = 0; | ||
109 | uint8_t* name = NULL; | ||
110 | |||
111 | while (OBEX_ObjectGetNextHeader (handle, object, &hi, &hv, &hlen)) { | ||
112 | switch(hi) { | ||
113 | case OBEX_HDR_BODY: | ||
114 | body = hv.bs; | ||
115 | body_len = hlen; | ||
116 | break; | ||
117 | |||
118 | case OBEX_HDR_NAME: | ||
119 | name = new uint8_t[(hlen / 2) + 1]; | ||
120 | OBEX_UnicodeToChar(name, hv.bs, hlen); | ||
121 | break; | ||
122 | |||
123 | default: | ||
124 | break; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | if (body) | ||
129 | err = file_received(name, body, body_len); | ||
130 | |||
131 | if (name) | ||
132 | delete[] name; | ||
133 | return err; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Function handles OBEX request | ||
138 | * @param handle OBEX connection handle | ||
139 | * @param object OBEX object itself | ||
140 | * @param mode | ||
141 | * @param event event code | ||
142 | * @param cmd OBEX command itself | ||
143 | */ | ||
144 | static void handle_request (obex_t* handle, obex_object_t* object, | ||
145 | int event, int cmd) | ||
146 | { | ||
147 | (void)event; | ||
148 | switch(cmd){ | ||
149 | case OBEX_CMD_SETPATH: | ||
150 | OBEX_ObjectSetRsp (object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); | ||
151 | break; | ||
152 | case OBEX_CMD_PUT: | ||
153 | if (put_done (handle, object) < 0) | ||
154 | OBEX_ObjectSetRsp (object, OBEX_RSP_INTERNAL_SERVER_ERROR, | ||
155 | OBEX_RSP_INTERNAL_SERVER_ERROR); | ||
156 | else | ||
157 | OBEX_ObjectSetRsp (object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); | ||
158 | break; | ||
159 | case OBEX_CMD_CONNECT: | ||
160 | OBEX_ObjectSetRsp (object, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS); | ||
161 | break; | ||
162 | case OBEX_CMD_DISCONNECT: | ||
163 | OBEX_ObjectSetRsp (object, OBEX_RSP_SUCCESS, OBEX_RSP_SUCCESS); | ||
164 | break; | ||
165 | default: | ||
166 | printf("Denied %02x request\n", cmd); | ||
167 | fflush(stdout); | ||
168 | OBEX_ObjectSetRsp (object, OBEX_RSP_NOT_IMPLEMENTED, | ||
169 | OBEX_RSP_NOT_IMPLEMENTED); | ||
170 | break; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | |||
175 | /** | ||
176 | * Function handles OBEX event when a client is connected to the server | ||
177 | * @param handle OBEX connection handle | ||
178 | * @param object OBEX object itself | ||
179 | * @param mode | ||
180 | * @param event event code | ||
181 | * @param obex_cmd OBEX command itself | ||
182 | * @param obex_rsp OBEX responce | ||
183 | */ | ||
184 | static void obex_conn_event (obex_t *handle, obex_object_t *object, | ||
185 | int mode, int event, int obex_cmd, int obex_rsp) | ||
186 | { | ||
187 | (void)mode; | ||
188 | (void)obex_rsp; | ||
189 | |||
190 | switch(event) { | ||
191 | case OBEX_EV_REQHINT: | ||
192 | switch(obex_cmd) { | ||
193 | case OBEX_CMD_PUT: | ||
194 | case OBEX_CMD_CONNECT: | ||
195 | case OBEX_CMD_DISCONNECT: | ||
196 | OBEX_ObjectSetRsp (object, OBEX_RSP_CONTINUE, OBEX_RSP_SUCCESS); | ||
197 | break; | ||
198 | default: | ||
199 | OBEX_ObjectSetRsp (object, OBEX_RSP_NOT_IMPLEMENTED, | ||
200 | OBEX_RSP_NOT_IMPLEMENTED); | ||
201 | break; | ||
202 | } | ||
203 | break; | ||
204 | |||
205 | case OBEX_EV_REQ: | ||
206 | /* Comes when a server-request has been received. */ | ||
207 | handle_request (handle, object, event, obex_cmd); | ||
208 | break; | ||
209 | |||
210 | case OBEX_EV_LINKERR: | ||
211 | break; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Function handles OBEX event | ||
217 | * @param handle OBEX connection handle | ||
218 | * @param object OBEX object itself | ||
219 | * @param mode | ||
220 | * @param event event code | ||
221 | * @param obex_cmd OBEX command itself | ||
222 | * @param obex_rsp OBEX responce | ||
223 | */ | ||
224 | static void obex_event (obex_t* handle, obex_object_t* object, int mode, | ||
225 | int event, int obex_cmd, int obex_rsp) | ||
226 | { | ||
227 | |||
228 | obex_t *obex; //OBEX connection handle | ||
229 | |||
230 | switch (event) { | ||
231 | case OBEX_EV_ACCEPTHINT: | ||
232 | obex = OBEX_ServerAccept (handle, obex_conn_event, NULL); | ||
233 | break; | ||
234 | |||
235 | default: | ||
236 | obex_conn_event(handle, object, mode, event, obex_cmd, obex_rsp); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | /** | ||
241 | * Function registers OBEX push service on a specified channel | ||
242 | * Based on The same function from GPE. | ||
243 | * @param session SDP session | ||
244 | * @param chan channel to listen | ||
245 | * @name name to show | ||
246 | */ | ||
247 | sdp_session_t* ObexServer::addOpushSvc(uint8_t chan, const char* name) | ||
248 | { | ||
249 | sdp_list_t *svclass_id, *pfseq, *apseq, *root; | ||
250 | uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid; | ||
251 | sdp_profile_desc_t profile[1]; | ||
252 | sdp_list_t *aproto, *proto[3]; | ||
253 | sdp_record_t record; | ||
254 | sdp_data_t *channel; | ||
255 | uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; | ||
256 | //uint8_t formats[] = { 0xff }; | ||
257 | void *dtds[sizeof(formats)], *values[sizeof(formats)]; | ||
258 | unsigned int i; | ||
259 | uint8_t dtd = SDP_UINT8; | ||
260 | sdp_data_t *sflist; | ||
261 | int err = 0; | ||
262 | sdp_session_t* lsession = 0; | ||
263 | |||
264 | memset((void *)&record, 0, sizeof(sdp_record_t)); | ||
265 | record.handle = 0xffffffff; | ||
266 | sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); | ||
267 | root = sdp_list_append(0, &root_uuid); | ||
268 | sdp_set_browse_groups(&record, root); | ||
269 | |||
270 | sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID); | ||
271 | svclass_id = sdp_list_append(0, &opush_uuid); | ||
272 | sdp_set_service_classes(&record, svclass_id); | ||
273 | |||
274 | sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); | ||
275 | profile[0].version = 0x0100; | ||
276 | pfseq = sdp_list_append(0, profile); | ||
277 | sdp_set_profile_descs(&record, pfseq); | ||
278 | |||
279 | sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); | ||
280 | proto[0] = sdp_list_append(0, &l2cap_uuid); | ||
281 | apseq = sdp_list_append(0, proto[0]); | ||
282 | |||
283 | sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); | ||
284 | proto[1] = sdp_list_append(0, &rfcomm_uuid); | ||
285 | channel = sdp_data_alloc(SDP_UINT8, &chan); | ||
286 | proto[1] = sdp_list_append(proto[1], channel); | ||
287 | apseq = sdp_list_append(apseq, proto[1]); | ||
288 | |||
289 | sdp_uuid16_create(&obex_uuid, OBEX_UUID); | ||
290 | proto[2] = sdp_list_append(0, &obex_uuid); | ||
291 | apseq = sdp_list_append(apseq, proto[2]); | ||
292 | |||
293 | aproto = sdp_list_append(0, apseq); | ||
294 | sdp_set_access_protos(&record, aproto); | ||
295 | |||
296 | for (i = 0; i < sizeof(formats); i++) | ||
297 | { | ||
298 | dtds[i] = &dtd; | ||
299 | values[i] = &formats[i]; | ||
300 | } | ||
301 | sflist = sdp_seq_alloc(dtds, values, sizeof(formats)); | ||
302 | sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist); | ||
303 | |||
304 | sdp_set_info_attr(&record, name, 0, 0); | ||
305 | |||
306 | // connect to the local SDP server, register the service record, and | ||
307 | // disconnect | ||
308 | lsession = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY); | ||
309 | if (lsession == NULL) | ||
310 | goto errout; | ||
311 | err = sdp_record_register(lsession, &record, 0); | ||
312 | if (err) { | ||
313 | sdp_close(lsession); | ||
314 | lsession = NULL; | ||
315 | } | ||
316 | errout: | ||
317 | sdp_data_free(channel); | ||
318 | sdp_list_free(proto[0], 0); | ||
319 | sdp_list_free(proto[1], 0); | ||
320 | sdp_list_free(proto[2], 0); | ||
321 | sdp_list_free(apseq, 0); | ||
322 | sdp_list_free(aproto, 0); | ||
323 | |||
324 | return lsession; | ||
325 | } | ||
326 | |||
327 | int ObexServer::initObex(void) | ||
328 | { | ||
329 | int channel = 10; //Channel on which we do listen | ||
330 | if (m_obex) | ||
331 | return 0; | ||
332 | m_obex = ::OBEX_Init(OBEX_TRANS_BLUETOOTH, obex_event, 0); | ||
333 | if (!m_obex) { | ||
334 | printf("OBEX initialization error %d\n", errno); | ||
335 | return -1; | ||
336 | } | ||
337 | ::BtOBEX_ServerRegister(m_obex, NULL, channel); | ||
338 | m_session = addOpushSvc(channel, "OBEX push service"); | ||
339 | if (!m_session) { | ||
340 | printf("OBEX registration error %d\n", errno); | ||
341 | ::OBEX_Cleanup(m_obex); | ||
342 | m_obex = NULL; | ||
343 | return -1; | ||
344 | } | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | bool ObexServer::start(RunMode runmode, Communication comm) | ||
349 | { | ||
350 | if ( runs ) | ||
351 | { | ||
352 | return false; // cannot start a process that is already running | ||
353 | // or if no executable has been assigned | ||
354 | } | ||
355 | run_mode = runmode; | ||
356 | status = 0; | ||
357 | |||
358 | if ( !setupCommunication( comm ) ) | ||
359 | qWarning( "Could not setup Communication!" ); | ||
360 | |||
361 | // We do this in the parent because if we do it in the child process | ||
362 | // gdb gets confused when the application runs from gdb. | ||
363 | uid_t uid = getuid(); | ||
364 | gid_t gid = getgid(); | ||
365 | #ifdef HAVE_INITGROUPS | ||
366 | |||
367 | struct passwd *pw = getpwuid( uid ); | ||
368 | #endif | ||
369 | |||
370 | int fd[ 2 ]; | ||
371 | if ( 0 > pipe( fd ) ) | ||
372 | { | ||
373 | fd[ 0 ] = fd[ 1 ] = 0; // Pipe failed.. continue | ||
374 | } | ||
375 | |||
376 | runs = true; | ||
377 | |||
378 | QApplication::flushX(); | ||
379 | |||
380 | // WABA: Note that we use fork() and not vfork() because | ||
381 | // vfork() has unclear semantics and is not standardized. | ||
382 | pid_ = fork(); | ||
383 | |||
384 | if ( 0 == pid_ ) | ||
385 | { | ||
386 | if ( fd[ 0 ] ) | ||
387 | close( fd[ 0 ] ); | ||
388 | if ( !runPrivileged() ) | ||
389 | { | ||
390 | setgid( gid ); | ||
391 | #if defined( HAVE_INITGROUPS) | ||
392 | |||
393 | if ( pw ) | ||
394 | initgroups( pw->pw_name, pw->pw_gid ); | ||
395 | #endif | ||
396 | |||
397 | setuid( uid ); | ||
398 | } | ||
399 | // The child process | ||
400 | if ( !commSetupDoneC() ) | ||
401 | qWarning( "Could not finish comm setup in child!" ); | ||
402 | |||
403 | setupEnvironment(); | ||
404 | |||
405 | // Matthias | ||
406 | if ( run_mode == DontCare ) | ||
407 | setpgid( 0, 0 ); | ||
408 | // restore default SIGPIPE handler (Harri) | ||
409 | struct sigaction act; | ||
410 | sigemptyset( &( act.sa_mask ) ); | ||
411 | sigaddset( &( act.sa_mask ), SIGPIPE ); | ||
412 | act.sa_handler = SIG_DFL; | ||
413 | act.sa_flags = 0; | ||
414 | sigaction( SIGPIPE, &act, 0L ); | ||
415 | |||
416 | // We set the close on exec flag. | ||
417 | // Closing of fd[1] indicates that the execvp succeeded! | ||
418 | if ( fd[ 1 ] ) | ||
419 | fcntl( fd[ 1 ], F_SETFD, FD_CLOEXEC ); | ||
420 | |||
421 | if (initObex() == 0) { | ||
422 | do { | ||
423 | int result; //Connection result | ||
424 | if ( fd[ 1 ] ) { | ||
425 | ::close(fd[1]); | ||
426 | fd[1] = 0; | ||
427 | } | ||
428 | if ((result = OBEX_HandleInput(m_obex, 60)) < 0) { | ||
429 | if (errno != ECONNRESET) { | ||
430 | printf("OBEX_HandleInput error %d\n", errno); | ||
431 | fflush(stdout); | ||
432 | _exit(-1); | ||
433 | } | ||
434 | else | ||
435 | _exit(0); | ||
436 | } | ||
437 | } while(1); | ||
438 | } | ||
439 | char resultByte = 1; | ||
440 | if ( fd[ 1 ] ) | ||
441 | write( fd[ 1 ], &resultByte, 1 ); | ||
442 | _exit( -1 ); | ||
443 | } | ||
444 | else if ( -1 == pid_ ) | ||
445 | { | ||
446 | // forking failed | ||
447 | |||
448 | runs = false; | ||
449 | return false; | ||
450 | } | ||
451 | else | ||
452 | { | ||
453 | if ( fd[ 1 ] ) | ||
454 | close( fd[ 1 ] ); | ||
455 | // the parent continues here | ||
456 | |||
457 | // Discard any data for stdin that might still be there | ||
458 | input_data = 0; | ||
459 | |||
460 | // Check whether client could be started. | ||
461 | if ( fd[ 0 ] ) | ||
462 | for ( ;; ) | ||
463 | { | ||
464 | char resultByte; | ||
465 | int n = ::read( fd[ 0 ], &resultByte, 1 ); | ||
466 | if ( n == 1 ) | ||
467 | { | ||
468 | // Error | ||
469 | runs = false; | ||
470 | close( fd[ 0 ] ); | ||
471 | pid_ = 0; | ||
472 | return false; | ||
473 | } | ||
474 | if ( n == -1 ) | ||
475 | { | ||
476 | if ( ( errno == ECHILD ) || ( errno == EINTR ) ) | ||
477 | continue; // Ignore | ||
478 | } | ||
479 | break; // success | ||
480 | } | ||
481 | if ( fd[ 0 ] ) | ||
482 | close( fd[ 0 ] ); | ||
483 | |||
484 | if ( !commSetupDoneP() ) // finish communication socket setup for the parent | ||
485 | qWarning( "Could not finish comm setup in parent!" ); | ||
486 | |||
487 | if ( run_mode == Block ) | ||
488 | { | ||
489 | commClose(); | ||
490 | |||
491 | // The SIGCHLD handler of the process controller will catch | ||
492 | // the exit and set the status | ||
493 | while ( runs ) | ||
494 | { | ||
495 | OProcessController::theOProcessController-> | ||
496 | slotDoHousekeeping( 0 ); | ||
497 | } | ||
498 | runs = FALSE; | ||
499 | emit processExited( this ); | ||
500 | } | ||
501 | } | ||
502 | return true; | ||
503 | } | ||
504 | |||
505 | /* | ||
506 | * Stop forwarding process | ||
507 | */ | ||
508 | int ObexServer::stop() | ||
509 | { | ||
510 | kill(SIGTERM); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | //eof | ||