2009-11-03 22:05:00 +08:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* OBEX Server
|
|
|
|
*
|
2010-01-07 18:32:49 +08:00
|
|
|
* Copyright (C) 2007-2010 Nokia Corporation
|
2010-01-02 09:09:14 +08:00
|
|
|
* Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
|
2009-11-03 22:05:00 +08:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include <openobex/obex.h>
|
|
|
|
#include <openobex/obex_const.h>
|
|
|
|
|
|
|
|
#include "plugin.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "obex.h"
|
|
|
|
#include "dbus.h"
|
|
|
|
#include "mimetype.h"
|
|
|
|
#include "service.h"
|
|
|
|
|
|
|
|
#define LST_TYPE "x-obex/folder-listing"
|
|
|
|
#define CAP_TYPE "x-obex/capability"
|
|
|
|
|
|
|
|
#define FTP_CHANNEL 10
|
|
|
|
#define FTP_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
|
|
|
|
<record> \
|
|
|
|
<attribute id=\"0x0001\"> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x1106\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0004\"> \
|
|
|
|
<sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0100\"/> \
|
|
|
|
</sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0003\"/> \
|
|
|
|
<uint8 value=\"%u\" name=\"channel\"/> \
|
|
|
|
</sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0008\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0009\"> \
|
|
|
|
<sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x1106\"/> \
|
|
|
|
<uint16 value=\"0x0100\" name=\"version\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0100\"> \
|
|
|
|
<text value=\"%s\" name=\"name\"/> \
|
|
|
|
</attribute> \
|
|
|
|
</record>"
|
|
|
|
|
|
|
|
#define PCSUITE_CHANNEL 24
|
2010-02-22 23:36:51 +08:00
|
|
|
#define PCSUITE_WHO_SIZE 8
|
2009-11-03 22:05:00 +08:00
|
|
|
#define PCSUITE_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
|
|
|
|
<record> \
|
|
|
|
<attribute id=\"0x0001\"> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0004\"> \
|
|
|
|
<sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0100\"/> \
|
|
|
|
</sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0003\"/> \
|
|
|
|
<uint8 value=\"%u\" name=\"channel\"/> \
|
|
|
|
</sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x0008\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0005\"> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"0x1002\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0009\"> \
|
|
|
|
<sequence> \
|
|
|
|
<sequence> \
|
|
|
|
<uuid value=\"00005005-0000-1000-8000-0002ee000001\"/> \
|
|
|
|
<uint16 value=\"0x0100\" name=\"version\"/> \
|
|
|
|
</sequence> \
|
|
|
|
</sequence> \
|
|
|
|
</attribute> \
|
|
|
|
\
|
|
|
|
<attribute id=\"0x0100\"> \
|
|
|
|
<text value=\"%s\" name=\"name\"/> \
|
|
|
|
</attribute> \
|
|
|
|
</record>"
|
|
|
|
|
|
|
|
static const guint8 FTP_TARGET[TARGET_SIZE] = {
|
|
|
|
0xF9, 0xEC, 0x7B, 0xC4, 0x95, 0x3C, 0x11, 0xD2,
|
|
|
|
0x98, 0x4E, 0x52, 0x54, 0x00, 0xDC, 0x9E, 0x09 };
|
|
|
|
|
2010-02-22 23:36:51 +08:00
|
|
|
static const guint8 PCSUITE_WHO[PCSUITE_WHO_SIZE] = {
|
|
|
|
'P','C',' ','S','u','i','t','e' };
|
2009-12-07 20:57:57 +08:00
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session {
|
|
|
|
struct obex_session *os;
|
|
|
|
gchar *folder;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void set_folder(struct ftp_session *ftp, const char *new_folder)
|
|
|
|
{
|
|
|
|
g_free(ftp->folder);
|
|
|
|
|
|
|
|
ftp->folder = new_folder ? g_strdup(new_folder) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint get_by_type(struct ftp_session *ftp, const gchar *type)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct obex_session *os = ftp->os;
|
2010-02-27 03:32:13 +08:00
|
|
|
const char *capability = obex_get_capability_path(os);
|
|
|
|
|
2009-11-03 22:05:00 +08:00
|
|
|
if (type == NULL)
|
2010-04-02 07:09:34 +08:00
|
|
|
return obex_get_stream_start(os, ftp->folder);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
|
|
|
if (g_str_equal(type, CAP_TYPE))
|
2010-04-02 07:09:34 +08:00
|
|
|
return obex_get_stream_start(os, capability);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
|
|
|
if (g_str_equal(type, LST_TYPE))
|
2010-04-02 07:09:34 +08:00
|
|
|
return obex_get_stream_start(os, ftp->folder);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-02-15 19:33:06 +08:00
|
|
|
return -ENOENT;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static gpointer ftp_connect(struct obex_session *os, int *err)
|
2009-11-10 03:00:41 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp;
|
|
|
|
const char *root_folder;
|
|
|
|
|
|
|
|
root_folder = obex_get_root_folder(os);
|
|
|
|
|
2010-02-26 04:24:59 +08:00
|
|
|
manager_register_session(os);
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
ftp = g_new0(struct ftp_session, 1);
|
|
|
|
set_folder(ftp, root_folder);
|
|
|
|
ftp->os = os;
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
if (err)
|
|
|
|
*err = 0;
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
return ftp;
|
2009-11-10 03:00:41 +08:00
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static int ftp_get(struct obex_session *os, obex_object_t *obj,
|
2010-03-19 06:53:05 +08:00
|
|
|
gboolean *stream, gpointer user_data)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp = user_data;
|
2010-02-27 03:32:13 +08:00
|
|
|
const char *type = obex_get_type(os);
|
2010-03-24 06:36:09 +08:00
|
|
|
int ret;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-24 06:36:09 +08:00
|
|
|
if (ftp->folder == NULL)
|
|
|
|
return -ENOENT;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-24 06:36:09 +08:00
|
|
|
ret = get_by_type(ftp, type);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-19 06:53:05 +08:00
|
|
|
if (stream)
|
|
|
|
*stream = TRUE;
|
|
|
|
|
2010-03-03 08:03:54 +08:00
|
|
|
return 0;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
static gint ftp_delete(struct ftp_session *ftp, const char *name)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
|
|
|
gchar *path;
|
|
|
|
int ret = 0;
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
if (!(ftp->folder && name))
|
2009-11-03 22:05:00 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
path = g_build_filename(ftp->folder, name, NULL);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
if (obex_remove(ftp->os, path) < 0)
|
2009-11-03 22:05:00 +08:00
|
|
|
ret = -errno;
|
|
|
|
|
|
|
|
g_free(path);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static gint ftp_chkput(struct obex_session *os, gpointer user_data)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp = user_data;
|
|
|
|
const gchar *name = obex_get_name(os);
|
|
|
|
gchar *path;
|
|
|
|
int ret;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-05-14 16:00:19 +08:00
|
|
|
if (name == NULL)
|
|
|
|
return -EBADR;
|
|
|
|
|
2010-02-27 02:51:54 +08:00
|
|
|
if (obex_get_size(os) == OBJECT_SIZE_DELETE)
|
2009-11-03 22:05:00 +08:00
|
|
|
return 0;
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
path = g_build_filename(ftp->folder, name, NULL);
|
|
|
|
|
2010-04-02 07:09:34 +08:00
|
|
|
ret = obex_put_stream_start(os, path);
|
2010-03-16 01:41:47 +08:00
|
|
|
|
|
|
|
g_free(path);
|
|
|
|
|
|
|
|
return ret;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static int ftp_put(struct obex_session *os, gpointer user_data)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp = user_data;
|
2010-02-27 01:36:27 +08:00
|
|
|
const char *name = obex_get_name(os);
|
|
|
|
ssize_t size = obex_get_size(os);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
if (ftp->folder == NULL)
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EPERM;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-02-27 01:22:07 +08:00
|
|
|
if (name == NULL)
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EBADR;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-02-27 01:22:07 +08:00
|
|
|
if (size == OBJECT_SIZE_DELETE)
|
2010-03-16 01:41:47 +08:00
|
|
|
return ftp_delete(ftp, name);
|
2010-03-03 08:03:54 +08:00
|
|
|
|
|
|
|
return 0;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static int ftp_setpath(struct obex_session *os, obex_object_t *obj,
|
|
|
|
gpointer user_data)
|
2009-11-03 22:05:00 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp = user_data;
|
|
|
|
const gchar *root_folder, *name;
|
2009-11-03 22:05:00 +08:00
|
|
|
guint8 *nonhdr;
|
|
|
|
gchar *fullname;
|
|
|
|
struct stat dstat;
|
|
|
|
gboolean root;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (OBEX_ObjectGetNonHdrData(obj, &nonhdr) != 2) {
|
|
|
|
error("Set path failed: flag and constants not found!");
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EBADMSG;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-02-27 01:36:27 +08:00
|
|
|
name = obex_get_name(os);
|
2010-02-26 23:21:04 +08:00
|
|
|
root_folder = obex_get_root_folder(os);
|
2010-03-16 01:41:47 +08:00
|
|
|
root = g_str_equal(root_folder, ftp->folder);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
|
|
|
/* Check flag "Backup" */
|
|
|
|
if ((nonhdr[0] & 0x01) == 0x01) {
|
|
|
|
debug("Set to parent path");
|
|
|
|
|
2010-02-26 23:21:04 +08:00
|
|
|
if (root)
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EPERM;
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
fullname = g_path_get_dirname(ftp->folder);
|
|
|
|
set_folder(ftp, fullname);
|
2009-11-03 22:05:00 +08:00
|
|
|
g_free(fullname);
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
debug("Set to parent path: %s", ftp->folder);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
2010-03-03 08:03:54 +08:00
|
|
|
return 0;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-02-26 23:21:04 +08:00
|
|
|
if (!name) {
|
2009-11-03 22:05:00 +08:00
|
|
|
debug("Set path failed: name missing!");
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EINVAL;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-02-26 23:21:04 +08:00
|
|
|
if (strlen(name) == 0) {
|
2009-11-03 22:05:00 +08:00
|
|
|
debug("Set to root");
|
2010-03-16 01:41:47 +08:00
|
|
|
set_folder(ftp, root_folder);
|
2010-03-03 08:03:54 +08:00
|
|
|
return 0;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check and set to name path */
|
2010-02-26 23:21:04 +08:00
|
|
|
if (strstr(name, "/") || strcmp(name, "..") == 0) {
|
2009-11-03 22:05:00 +08:00
|
|
|
error("Set path failed: name incorrect!");
|
2010-03-03 08:03:54 +08:00
|
|
|
return -EPERM;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-16 01:41:47 +08:00
|
|
|
fullname = g_build_filename(ftp->folder, name, NULL);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
|
|
|
debug("Fullname: %s", fullname);
|
|
|
|
|
2010-02-26 23:21:04 +08:00
|
|
|
if (root && obex_get_symlinks(os))
|
2009-11-03 22:05:00 +08:00
|
|
|
err = stat(fullname, &dstat);
|
|
|
|
else
|
|
|
|
err = lstat(fullname, &dstat);
|
|
|
|
|
|
|
|
if (err < 0) {
|
2010-04-27 21:25:39 +08:00
|
|
|
err = -errno;
|
|
|
|
|
|
|
|
if (err == -ENOENT)
|
2009-11-03 22:05:00 +08:00
|
|
|
goto not_found;
|
|
|
|
|
2010-04-27 21:25:39 +08:00
|
|
|
debug("%s: %s(%d)", root ? "stat" : "lstat",
|
|
|
|
strerror(-err), -err);
|
|
|
|
|
2009-11-03 22:05:00 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISDIR(dstat.st_mode) && (dstat.st_mode & S_IRUSR) &&
|
|
|
|
(dstat.st_mode & S_IXUSR)) {
|
2010-03-16 01:41:47 +08:00
|
|
|
set_folder(ftp, fullname);
|
2009-11-03 22:05:00 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2010-03-03 08:03:54 +08:00
|
|
|
err = -EPERM;
|
2009-11-03 22:05:00 +08:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
not_found:
|
|
|
|
if (nonhdr[0] != 0) {
|
2010-03-03 08:03:54 +08:00
|
|
|
err = -ENOENT;
|
2009-11-03 22:05:00 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mkdir(fullname, 0755) < 0) {
|
2010-04-27 21:25:39 +08:00
|
|
|
err = -errno;
|
|
|
|
debug("mkdir: %s(%d)", strerror(-err), -err);
|
2009-11-03 22:05:00 +08:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2010-04-27 21:25:39 +08:00
|
|
|
err = 0;
|
2010-03-16 01:41:47 +08:00
|
|
|
set_folder(ftp, fullname);
|
2009-11-03 22:05:00 +08:00
|
|
|
|
|
|
|
done:
|
|
|
|
g_free(fullname);
|
2010-03-03 08:03:54 +08:00
|
|
|
return err;
|
2009-11-03 22:05:00 +08:00
|
|
|
}
|
|
|
|
|
2010-03-06 07:18:36 +08:00
|
|
|
static void ftp_disconnect(struct obex_session *os, gpointer user_data)
|
2009-11-10 03:00:41 +08:00
|
|
|
{
|
2010-03-16 01:41:47 +08:00
|
|
|
struct ftp_session *ftp = user_data;
|
|
|
|
|
2010-02-26 04:57:49 +08:00
|
|
|
manager_unregister_session(os);
|
2010-03-16 01:41:47 +08:00
|
|
|
|
|
|
|
g_free(ftp->folder);
|
|
|
|
g_free(ftp);
|
2009-11-10 03:00:41 +08:00
|
|
|
}
|
|
|
|
|
2010-04-01 05:09:51 +08:00
|
|
|
static struct obex_service_driver pcsuite = {
|
2009-11-03 22:05:00 +08:00
|
|
|
.name = "Nokia OBEX PC Suite Services",
|
|
|
|
.service = OBEX_PCSUITE,
|
|
|
|
.channel = PCSUITE_CHANNEL,
|
|
|
|
.record = PCSUITE_RECORD,
|
|
|
|
.target = FTP_TARGET,
|
2009-11-16 15:17:47 +08:00
|
|
|
.target_size = TARGET_SIZE,
|
2010-02-22 23:36:51 +08:00
|
|
|
.who = PCSUITE_WHO,
|
|
|
|
.who_size = PCSUITE_WHO_SIZE,
|
2009-11-10 03:00:41 +08:00
|
|
|
.connect = ftp_connect,
|
2009-11-03 22:05:00 +08:00
|
|
|
.get = ftp_get,
|
|
|
|
.put = ftp_put,
|
|
|
|
.chkput = ftp_chkput,
|
2009-11-10 03:00:41 +08:00
|
|
|
.setpath = ftp_setpath,
|
|
|
|
.disconnect = ftp_disconnect
|
2009-11-03 22:05:00 +08:00
|
|
|
};
|
|
|
|
|
2010-04-01 05:09:51 +08:00
|
|
|
static struct obex_service_driver ftp = {
|
2009-11-03 22:05:00 +08:00
|
|
|
.name = "File Transfer server",
|
|
|
|
.service = OBEX_FTP,
|
|
|
|
.channel = FTP_CHANNEL,
|
|
|
|
.record = FTP_RECORD,
|
|
|
|
.target = FTP_TARGET,
|
2009-11-16 15:17:47 +08:00
|
|
|
.target_size = TARGET_SIZE,
|
2009-11-10 03:00:41 +08:00
|
|
|
.connect = ftp_connect,
|
2009-11-03 22:05:00 +08:00
|
|
|
.get = ftp_get,
|
|
|
|
.put = ftp_put,
|
|
|
|
.chkput = ftp_chkput,
|
2009-11-10 03:00:41 +08:00
|
|
|
.setpath = ftp_setpath,
|
|
|
|
.disconnect = ftp_disconnect
|
2009-11-03 22:05:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ftp_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = obex_service_driver_register(&ftp);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return obex_service_driver_register(&pcsuite);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ftp_exit(void)
|
|
|
|
{
|
|
|
|
obex_service_driver_unregister(&ftp);
|
|
|
|
obex_service_driver_unregister(&pcsuite);
|
|
|
|
}
|
|
|
|
|
2009-11-03 22:11:19 +08:00
|
|
|
OBEX_PLUGIN_DEFINE(ftp, ftp_init, ftp_exit)
|