1
# ubuntuone.eventlog.zg_listener - listen for SD events, log into ZG
3
# Author: Alejandro J. Cura <alecu@canonical.com>
5
# Copyright 2010 Canonical Ltd.
7
# This program is free software: you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License version 3, as published
9
# by the Free Software Foundation.
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranties of
13
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
# PURPOSE. See the GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License along
17
# with this program. If not, see <http://www.gnu.org/licenses/>.
18
"""Event logging from SyncDaemon into Zeitgeist."""
22
from zeitgeist.datamodel import Event, Interpretation, Manifestation, Subject
23
from zeitgeist.mimetypes import get_interpretation_for_mimetype
25
from ubuntuone.eventlog import zglog
26
from ubuntuone.syncdaemon.volume_manager import Share, UDF
28
ACTOR_UBUNTUONE = "dbus://com.ubuntuone.SyncDaemon.service"
29
DIRECTORY_MIMETYPE = "inode/directory"
30
DEFAULT_MIME = "application/octet-stream"
31
DEFAULT_INTERPRETATION = Interpretation.DOCUMENT
32
EVENT_INTERPRETATION_U1_FOLDER_SHARED = "u1://FolderShared"
33
EVENT_INTERPRETATION_U1_FOLDER_UNSHARED = "u1://FolderUnshared"
34
EVENT_INTERPRETATION_U1_SHARE_ACCEPTED = "u1://ShareAccepted"
35
EVENT_INTERPRETATION_U1_SHARE_UNACCEPTED = "u1://ShareUnaccepted"
36
EVENT_INTERPRETATION_U1_CONFLICT_RENAME = "u1://ConflictRename"
37
EVENT_INTERPRETATION_U1_UDF_CREATED = "u1://UserFolderCreated"
38
EVENT_INTERPRETATION_U1_UDF_DELETED = "u1://UserFolderDeleted"
39
EVENT_INTERPRETATION_U1_UDF_SUBSCRIBED = "u1://UserFolderSubscribed"
40
EVENT_INTERPRETATION_U1_UDF_UNSUBSCRIBED = "u1://UserFolderUnsubscribed"
41
MANIFESTATION_U1_CONTACT_DATA_OBJECT = "u1://ContactDataObject"
42
INTERPRETATION_U1_CONTACT = "u1://Contact"
43
URI_PROTOCOL_U1 = "ubuntuone:"
45
STORAGE_NETWORK = "net"
46
STORAGE_DELETED = "deleted"
48
class ZeitgeistListener(object):
49
"""An Event Queue listener that logs into ZG."""
51
def __init__(self, fsm, vm):
52
"""Initialize this instance."""
55
self.zg = zglog.ZeitgeistLogger()
56
self.newly_created_server_files = set()
57
self.newly_created_local_files = set()
59
def handle_AQ_CREATE_SHARE_OK(self, share_id=None, marker=None):
60
"""Log the 'directory shared thru the server' event."""
61
share = self.vm.shared[share_id]
62
self.log_folder_shared(share, share_id)
64
def handle_AQ_SHARE_INVITATION_SENT(self, marker):
65
"""Log the 'directory shared thru http' event."""
66
share = self.vm.marker_share_map[marker]
67
mdo = self.fsm.get_by_mdid(marker)
68
self.log_folder_shared(share, mdo.share_id)
70
def log_folder_shared(self, share, share_id):
71
"""Log the 'directory shared' event."""
72
fullpath = self.fsm.get_abspath(share_id, share.path)
74
folder = Subject.new_for_values(
75
uri=URI_PROTOCOL_U1 + str(share.node_id),
76
interpretation=Interpretation.FOLDER,
77
manifestation=Manifestation.REMOTE_DATA_OBJECT,
78
origin="file:///" + fullpath,
79
mimetype=DIRECTORY_MIMETYPE,
80
storage=STORAGE_NETWORK)
82
other_username = share.other_username
83
other_user = Subject.new_for_values(
84
uri="mailto:" + other_username,
85
interpretation=INTERPRETATION_U1_CONTACT,
86
manifestation=MANIFESTATION_U1_CONTACT_DATA_OBJECT)
88
event = Event.new_for_values(
89
interpretation=EVENT_INTERPRETATION_U1_FOLDER_SHARED,
90
manifestation=Manifestation.USER_ACTIVITY,
91
actor=ACTOR_UBUNTUONE,
92
subjects=[folder, other_user])
96
def handle_VM_SHARE_DELETED(self, share):
97
"""Log the share deleted event."""
98
folder = Subject.new_for_values(
99
uri=URI_PROTOCOL_U1 + str(share.node_id),
100
interpretation=Interpretation.FOLDER,
101
manifestation=Manifestation.REMOTE_DATA_OBJECT,
102
origin="file:///" + share.path,
103
mimetype=DIRECTORY_MIMETYPE,
104
storage=STORAGE_NETWORK)
106
other_username = share.other_username
107
other_user = Subject.new_for_values(
108
uri="mailto:" + other_username,
109
interpretation=INTERPRETATION_U1_CONTACT,
110
manifestation=MANIFESTATION_U1_CONTACT_DATA_OBJECT)
112
event = Event.new_for_values(
113
interpretation=EVENT_INTERPRETATION_U1_FOLDER_UNSHARED,
114
manifestation=Manifestation.USER_ACTIVITY,
115
actor=ACTOR_UBUNTUONE,
116
subjects=[folder, other_user])
120
def handle_VM_SHARE_CREATED(self, share_id):
121
"""Log the share accepted event."""
122
share = self.vm.shares[share_id]
124
folder = Subject.new_for_values(
125
uri=URI_PROTOCOL_U1 + str(share.node_id),
126
interpretation=Interpretation.FOLDER,
127
manifestation=Manifestation.REMOTE_DATA_OBJECT,
128
origin="file:///" + share.path,
129
mimetype=DIRECTORY_MIMETYPE,
130
storage=STORAGE_NETWORK)
132
other_username = share.other_username
133
other_user = Subject.new_for_values(
134
uri="mailto:" + other_username,
135
interpretation=INTERPRETATION_U1_CONTACT,
136
manifestation=MANIFESTATION_U1_CONTACT_DATA_OBJECT)
138
event = Event.new_for_values(
139
interpretation=EVENT_INTERPRETATION_U1_SHARE_ACCEPTED,
140
manifestation=Manifestation.USER_ACTIVITY,
141
actor=ACTOR_UBUNTUONE,
142
subjects=[folder, other_user])
146
def log_share_unaccepted(self, share):
147
"""Log the share unaccepted event."""
148
folder = Subject.new_for_values(
149
uri=URI_PROTOCOL_U1 + str(share.node_id),
150
interpretation=Interpretation.FOLDER,
151
manifestation=Manifestation.REMOTE_DATA_OBJECT,
152
origin="file:///" + share.path,
153
mimetype=DIRECTORY_MIMETYPE,
154
storage=STORAGE_NETWORK)
156
other_username = share.other_username
157
other_user = Subject.new_for_values(
158
uri="mailto:" + other_username,
159
interpretation=INTERPRETATION_U1_CONTACT,
160
manifestation=MANIFESTATION_U1_CONTACT_DATA_OBJECT)
162
event = Event.new_for_values(
163
interpretation=EVENT_INTERPRETATION_U1_SHARE_UNACCEPTED,
164
manifestation=Manifestation.USER_ACTIVITY,
165
actor=ACTOR_UBUNTUONE,
166
subjects=[folder, other_user])
170
def log_udf_deleted(self, volume):
171
"""Log the udf deleted event."""
172
folder = Subject.new_for_values(
173
uri=URI_PROTOCOL_U1 + str(volume.node_id),
174
interpretation=Interpretation.FOLDER,
175
manifestation=Manifestation.DELETED_RESOURCE,
176
origin="file:///" + volume.path,
177
mimetype=DIRECTORY_MIMETYPE,
178
storage=STORAGE_DELETED)
180
event = Event.new_for_values(
181
interpretation=EVENT_INTERPRETATION_U1_UDF_DELETED,
182
manifestation=Manifestation.USER_ACTIVITY,
183
actor=ACTOR_UBUNTUONE,
188
def handle_VM_VOLUME_DELETED(self, volume):
189
"""Log the share/UDF unaccepted event."""
190
if isinstance(volume, Share):
191
self.log_share_unaccepted(volume)
192
if isinstance(volume, UDF):
193
self.log_udf_deleted(volume)
195
def handle_VM_UDF_CREATED(self, udf):
196
"""An udf was created. Log it into Zeitgeist."""
197
folder = Subject.new_for_values(
198
uri=URI_PROTOCOL_U1 + str(udf.node_id),
199
interpretation=Interpretation.FOLDER,
200
manifestation=Manifestation.REMOTE_DATA_OBJECT,
201
origin="file:///" + udf.path,
202
mimetype=DIRECTORY_MIMETYPE,
203
storage=STORAGE_NETWORK)
205
event = Event.new_for_values(
206
interpretation=EVENT_INTERPRETATION_U1_UDF_CREATED,
207
manifestation=Manifestation.USER_ACTIVITY,
208
actor=ACTOR_UBUNTUONE,
213
def handle_VM_UDF_SUBSCRIBED(self, udf):
214
"""An udf was subscribed."""
216
folder = Subject.new_for_values(
217
uri="file:///" + udf.path,
218
interpretation=Interpretation.FOLDER,
219
manifestation=Manifestation.FILE_DATA_OBJECT,
220
origin=URI_PROTOCOL_U1 + str(udf.node_id),
221
mimetype=DIRECTORY_MIMETYPE,
222
storage=STORAGE_LOCAL)
224
event = Event.new_for_values(
225
interpretation=EVENT_INTERPRETATION_U1_UDF_SUBSCRIBED,
226
manifestation=Manifestation.USER_ACTIVITY,
227
actor=ACTOR_UBUNTUONE,
232
def handle_VM_UDF_UNSUBSCRIBED(self, udf):
233
"""An udf was unsubscribed."""
235
folder = Subject.new_for_values(
236
uri="file:///" + udf.path,
237
interpretation=Interpretation.FOLDER,
238
manifestation=Manifestation.DELETED_RESOURCE,
239
origin=URI_PROTOCOL_U1 + str(udf.node_id),
240
mimetype=DIRECTORY_MIMETYPE,
241
storage=STORAGE_DELETED)
243
event = Event.new_for_values(
244
interpretation=EVENT_INTERPRETATION_U1_UDF_UNSUBSCRIBED,
245
manifestation=Manifestation.USER_ACTIVITY,
246
actor=ACTOR_UBUNTUONE,
251
def handle_AQ_FILE_NEW_OK(self, volume_id, marker, new_id, new_generation):
252
"""A new file was created on server. Store and wait till it uploads."""
253
self.newly_created_server_files.add((volume_id, new_id))
255
def get_mime_and_interpretation_for_filepath(self, filepath):
256
"""Try to guess the mime and the interpretation from the path."""
257
mime, encoding = mimetypes.guess_type(filepath)
259
return DEFAULT_MIME, DEFAULT_INTERPRETATION
260
interpret = get_interpretation_for_mimetype(mime)
261
if interpret is None:
262
return DEFAULT_MIME, Interpretation.DOCUMENT
263
return mime, interpret
265
def handle_AQ_UPLOAD_FINISHED(self, share_id, node_id, hash,
267
"""A file finished uploading to the server."""
269
mdo = self.fsm.get_by_node_id(share_id, node_id)
270
path = self.fsm.get_abspath(share_id, mdo.path)
272
if (share_id, node_id) in self.newly_created_server_files:
273
self.newly_created_server_files.remove((share_id, node_id))
274
event_interpretation = Interpretation.CREATE_EVENT
276
event_interpretation = Interpretation.MODIFY_EVENT
278
mime, interp = self.get_mime_and_interpretation_for_filepath(path)
280
file_subject = Subject.new_for_values(
281
uri=URI_PROTOCOL_U1 + str(node_id),
282
interpretation=interp,
283
manifestation=Manifestation.REMOTE_DATA_OBJECT,
284
origin="file:///" + path,
286
storage=STORAGE_NETWORK)
288
event = Event.new_for_values(
289
interpretation=event_interpretation,
290
manifestation=Manifestation.SCHEDULED_ACTIVITY,
291
actor=ACTOR_UBUNTUONE,
292
subjects=[file_subject])
296
def handle_AQ_DIR_NEW_OK(self, volume_id, marker, new_id, new_generation):
297
"""A dir was created on the server."""
299
mdo = self.fsm.get_by_node_id(volume_id, new_id)
300
path = self.fsm.get_abspath(volume_id, mdo.path)
302
file_subject = Subject.new_for_values(
303
uri=URI_PROTOCOL_U1 + str(new_id),
304
interpretation=Interpretation.FOLDER,
305
manifestation=Manifestation.REMOTE_DATA_OBJECT,
306
origin="file:///" + path,
307
mimetype=DIRECTORY_MIMETYPE,
308
storage=STORAGE_NETWORK)
310
event = Event.new_for_values(
311
interpretation=Interpretation.CREATE_EVENT,
312
manifestation=Manifestation.SCHEDULED_ACTIVITY,
313
actor=ACTOR_UBUNTUONE,
314
subjects=[file_subject])
318
def handle_SV_FILE_NEW(self, volume_id, node_id, parent_id, name):
319
"""A file was created locally by Syncdaemon."""
320
self.newly_created_local_files.add((volume_id, node_id))
322
def handle_AQ_DOWNLOAD_FINISHED(self, share_id, node_id, server_hash):
323
"""A file finished downloading from the server."""
325
mdo = self.fsm.get_by_node_id(share_id, node_id)
326
path = self.fsm.get_abspath(share_id, mdo.path)
328
if (share_id, node_id) in self.newly_created_local_files:
329
self.newly_created_local_files.remove((share_id, node_id))
330
event_interpretation = Interpretation.CREATE_EVENT
332
event_interpretation = Interpretation.MODIFY_EVENT
334
mime, interp = self.get_mime_and_interpretation_for_filepath(path)
336
file_subject = Subject.new_for_values(
337
uri="file:///" + path,
338
interpretation=interp,
339
manifestation=Manifestation.FILE_DATA_OBJECT,
340
origin=URI_PROTOCOL_U1 + str(node_id),
342
storage=STORAGE_LOCAL)
344
event = Event.new_for_values(
345
interpretation=event_interpretation,
346
manifestation=Manifestation.WORLD_ACTIVITY,
347
actor=ACTOR_UBUNTUONE,
348
subjects=[file_subject])
352
def handle_SV_DIR_NEW(self, volume_id, node_id, parent_id, name):
353
"""A file finished downloading from the server."""
355
mdo = self.fsm.get_by_node_id(volume_id, node_id)
356
path = self.fsm.get_abspath(volume_id, mdo.path)
358
file_subject = Subject.new_for_values(
359
uri="file:///" + path,
360
interpretation=Interpretation.FOLDER,
361
manifestation=Manifestation.FILE_DATA_OBJECT,
362
origin=URI_PROTOCOL_U1 + str(node_id),
363
mimetype=DIRECTORY_MIMETYPE,
364
storage=STORAGE_LOCAL)
366
event = Event.new_for_values(
367
interpretation=Interpretation.CREATE_EVENT,
368
manifestation=Manifestation.WORLD_ACTIVITY,
369
actor=ACTOR_UBUNTUONE,
370
subjects=[file_subject])
374
def handle_SV_FILE_DELETED(self, volume_id, node_id, is_dir):
375
"""A file or folder was deleted locally by Syncdaemon."""
376
mdo = self.fsm.get_by_node_id(volume_id, node_id)
377
path = self.fsm.get_abspath(volume_id, mdo.path)
380
mime, interp = DIRECTORY_MIMETYPE, Interpretation.FOLDER
382
mime, interp = self.get_mime_and_interpretation_for_filepath(path)
384
file_subject = Subject.new_for_values(
385
uri="file:///" + path,
386
interpretation=interp,
387
manifestation=Manifestation.DELETED_RESOURCE,
388
origin=URI_PROTOCOL_U1 + str(node_id),
390
storage=STORAGE_DELETED)
392
event = Event.new_for_values(
393
interpretation=Interpretation.DELETE_EVENT,
394
manifestation=Manifestation.WORLD_ACTIVITY,
395
actor=ACTOR_UBUNTUONE,
396
subjects=[file_subject])
400
def handle_AQ_UNLINK_OK(self, share_id, parent_id, node_id,
402
"""A file or folder was deleted on the server by Syncdaemon,"""
403
mdo = self.fsm.get_by_node_id(share_id, node_id)
404
path = self.fsm.get_abspath(share_id, mdo.path)
407
mime, interp = DIRECTORY_MIMETYPE, Interpretation.FOLDER
409
mime, interp = self.get_mime_and_interpretation_for_filepath(path)
411
file_subject = Subject.new_for_values(
412
uri=URI_PROTOCOL_U1 + str(node_id),
413
interpretation=interp,
414
manifestation=Manifestation.DELETED_RESOURCE,
415
origin="file:///" + path,
417
storage=STORAGE_DELETED)
419
event = Event.new_for_values(
420
interpretation=Interpretation.DELETE_EVENT,
421
manifestation=Manifestation.SCHEDULED_ACTIVITY,
422
actor=ACTOR_UBUNTUONE,
423
subjects=[file_subject])
427
def handle_FSM_FILE_CONFLICT(self, old_name, new_name):
428
"""A file was renamed because of conflict."""
429
mime, interp = self.get_mime_and_interpretation_for_filepath(old_name)
431
file_subject = Subject.new_for_values(
432
uri="file:///" + new_name,
433
interpretation=interp,
434
manifestation=Manifestation.FILE_DATA_OBJECT,
435
origin="file:///" + old_name,
437
storage=STORAGE_LOCAL)
439
event = Event.new_for_values(
440
interpretation=EVENT_INTERPRETATION_U1_CONFLICT_RENAME,
441
manifestation=Manifestation.WORLD_ACTIVITY,
442
actor=ACTOR_UBUNTUONE,
443
subjects=[file_subject])
447
def handle_FSM_DIR_CONFLICT(self, old_name, new_name):
448
"""A dir was renamed because of conflict."""
449
folder_subject = Subject.new_for_values(
450
uri="file:///" + new_name,
451
interpretation=Interpretation.FOLDER,
452
manifestation=Manifestation.FILE_DATA_OBJECT,
453
origin="file:///" + old_name,
454
mimetype=DIRECTORY_MIMETYPE,
455
storage=STORAGE_LOCAL)
457
event = Event.new_for_values(
458
interpretation=EVENT_INTERPRETATION_U1_CONFLICT_RENAME,
459
manifestation=Manifestation.WORLD_ACTIVITY,
460
actor=ACTOR_UBUNTUONE,
461
subjects=[folder_subject])
465
def handle_AQ_CHANGE_PUBLIC_ACCESS_OK(self, share_id, node_id, is_public,
467
"""The status of a published resource changed. Log it!"""
469
self.log_publishing(share_id, node_id, is_public, public_url)
471
self.log_unpublishing(share_id, node_id, is_public, public_url)
473
def log_publishing(self, share_id, node_id, is_public, public_url):
474
"""Log the publishing of a resource."""
475
mime, interp = self.get_mime_and_interpretation_for_filepath(
478
origin = "" if node_id is None else URI_PROTOCOL_U1 + str(node_id)
480
public_file = Subject.new_for_values(
482
interpretation=interp,
483
manifestation=Manifestation.REMOTE_DATA_OBJECT,
486
storage=STORAGE_NETWORK)
488
event = Event.new_for_values(
489
interpretation=Interpretation.CREATE_EVENT,
490
manifestation=Manifestation.USER_ACTIVITY,
491
actor=ACTOR_UBUNTUONE,
492
subjects=[public_file])
496
def log_unpublishing(self, share_id, node_id, is_public, public_url):
497
"""Log the unpublishing of a resource."""
498
mime, interp = self.get_mime_and_interpretation_for_filepath(
501
origin = "" if node_id is None else URI_PROTOCOL_U1 + str(node_id)
503
public_file = Subject.new_for_values(
505
interpretation=interp,
506
manifestation=Manifestation.DELETED_RESOURCE,
509
storage=STORAGE_DELETED)
511
event = Event.new_for_values(
512
interpretation=Interpretation.DELETE_EVENT,
513
manifestation=Manifestation.USER_ACTIVITY,
514
actor=ACTOR_UBUNTUONE,
515
subjects=[public_file])