Commit Graph

46 Commits

Author SHA1 Message Date
Patrick Ohly 04f11b422e source -> datastore rename, improved terminology
The word "source" implies reading, while in fact access is read/write.
"datastore" avoids that misconception. Writing it in one word emphasizes
that it is single entity.

While renaming, also remove references to explicit --*-property
parameters. The only necessary use today is "--sync-property ?"
and "--datastore-property ?".

--datastore-property was used instead of the short --store-property
because "store" might be mistaken for the verb. It doesn't matter
that it is longer because it doesn't get typed often.

--source-property must remain valid for backward compatility.

As many user-visible instances of "source" as possible got replaced in
text strings by the newer term "datastore". Debug messages were left
unchanged unless some regex happened to match it.

The source code will continue to use the old variable and class names
based on "source".

Various documentation enhancements:
  Better explain what local sync is and how it involves two sync
  configs. "originating config" gets introduces instead of just
  "sync config".

  Better explain the relationship between contexts, sync configs,
  and source configs ("a sync config can use the datastore configs in
  the same context").

  An entire section on config properties in the terminology
  section. "item" added (Todd Wilson correctly pointed out that it was
  missing).

  Less focus on conflict resolution, as suggested by Graham Cobb.

  Fix examples that became invalid when fixing the password
  storage/lookup mechanism for GNOME keyring in 1.4.

  The "command line conventions", "Synchronization beyond SyncML" and
  "CalDAV and CardDAV" sections were updated. It's possible that the
  other sections also contain slightly incorrect usage of the
  terminology or are simply out-dated.
2014-07-28 15:29:41 +02:00
Patrick Ohly de8461f802 code restructing: Exception, throwError()
Raising exceptions via throwError() looses the original source code
location information. Fixing that by explicitly passing that
information as additional parameter, created with the preprocessor
macro SE_HERE.

Several files included the complex SyncContext.h only because needed
throwError(). A better place for the revised implementation is the
Exception class which used to be in util.h, now Exception.h.

Simplifying the include statements exposed indirect include
dependencies and removed "using namespace std" from the compilation of
some source files which happened to rely on it. We want to get rid of
that name space polution, so fix the code instead of adding it back.
2014-05-02 16:43:52 +02:00
Patrick Ohly 5ea8b597df sqlite: dead code removal
The "numargs" variable was only set and modified, but never used.
Must be an artifact, can be removed. Found by clang's scan-tool.
2014-01-17 16:15:16 +01:00
Patrick Ohly 4f8615ee8b Logging: eliminate _instance from SE_LOG* macros
With the _instance parameter always being NULL thanks to the previous
patch, it can be removed completely.
2013-05-06 16:28:13 +02:00
Patrick Ohly 7214834e0b autotools: avoid -lrt in make dependencies
The backends had SYNCEVOLUTION_LIBS in their _DEPENDENCIES entries,
which is wrong because SYNCEVOLUTION_LIBS must include -lrt (which
can't be a dependency).

Fixed by depending on libsyncevolution.la directly.
2013-02-26 12:03:45 +01:00
Mario Kicherer 57a6c5a214 sqlite: add #include <stdio.h>
Fails to compile on Gentoo with gcc 4.5.4 otherwise because the header
file is needed for sprintf.
2013-01-15 16:26:12 +01:00
Patrick Ohly 12334eb676 SyncSource: remove special RegisterSyncSource::InactiveSource pointer
The special semantic of the former RegisterSyncSource::InactiveSource
(invalid pointer of value 1) caused bugs, like using it in
--print-databases (=> segfault) or not being able to store the result
of a createSource() directly in a smart pointer (=> potential leak in
SyncSource::createSource()).

Obviously a bad idea to start with. Replaced with a
RegisterSyncSource::InactiveSource() method which creates a real,
inactive SyncSource instance which can and must be deleted by the
caller.

This is a SyncSource API change for backend developers.
Instead of RegisterSyncSource::InactiveSource, return
RegisterSyncSource::InactiveSource().

Comparisons against RegisterSyncSource::InactiveSource needs
to be replaced with a call to the new SyncSource::isInactive().

User visible fixes:
* --print-databases: no longer crashes when EDS or KDE backends
  are not usable. Instead it prints "not enabled during compilation or
  not usable in the current environment".
* --print-databases: continues with other backends even if
  one backend throws an exception, as the KDE backend does
  when it cannot find Akonadi. Error messages are printed.
2012-03-09 07:25:11 +00:00
Patrick Ohly ba289c899f phone sync: delete<->delete conflict + phone calendar+todo sync (BMC #23744)
When deleting an item on phone and locally, the next sync fails with
ERROR messages about "object not found". This has several reasons:
- libsynthesis super data store attempts to read items
  which may or may not exist (triggers ERROR message)
- it checks for 404 but Evolution backends only return a generic
  database error (causes sync to fail)

It turned out that ReadItem and DeleteItem are expected to return a
404 status when the requested item does not exist. This patch documents
that (only in the TrackingSyncSource, though), adds tests and fixes
EDS, WebDAV, file and sqlite backends accordingly.

This patch also suppresses the 404 error logging inside DeleteItem(),
while still returning that error code to the Synthesis engine. Not
logging that particular situation is consistent with the previous
SyncEvolution behavior of silently returning successfully when there
wasn't anything to delete.

In addition, more recent libsynthesis versions also no longer do
a ReadItem() call to test for existence. That would still trigger
a spurious (albeit now harmless) ERROR message.
2011-11-14 21:17:02 +01:00
Patrick Ohly 6399bd8181 testing: cleaned up ClientTestConfig
The memset/memcpy of the embedded boost::function instances inside the
old ClientTestConfig was causing segfaults at the end of a client-test
run if compiled with optimization.

Therefore this commit turns ClientTestConfig into a proper class
containing members which initialize themselves (Bool wrapper class,
std::string), thus memset is no longer needed and used. Also added the
standard m_ prefix.

m_numItems is gone, was never set by any backend anyway and even
expected to be consistent in one test. Now CLIENT_TEST_NUM_ITEMS is
read by defNumItems() each time it is needed.

Removed "const char *" strings from method parameters. This revealed
that config.itemType (a const char *) was incorrectly passed to
insert() where the boolean "relax" parameter should have been given.
Replaced by "false" (= strict checking) even though the old code
must have run with an implicit "true" (= relaxed checking). Let's see
whether any tests fail now.
2011-09-02 09:42:19 +02:00
Krzesimir Nowak a7ad98fabc Port build system to non-recursive Automake.
All but toplevel Makefile.am are replaced with their non-recursive
counterparts. The generation of configure.in was removed (and thus
configure-{pre,post}.in are also removed) in favor of configure.ac
and m4 macros adding backend specific configure parts.

Version number is generated like in old build system.

There are still many things to improve, but for now there are no
immediate regressions. AUTOTOOLS-TODO contains a list of possible
improvements and fixes. AUTOTOOLS-TESTING contains what was tested
with current build system (configure flags, make options).
2011-08-30 16:38:34 +02:00
Patrick Ohly a714b0b330 SQLite backend: fixed compilation error due to detectChanges() API change
The API was intentionally changed to notify backend developers of the
new possibilities. The SQLite backend hadn't been adapted and failed
to compile...
2011-06-27 07:42:49 -07:00
Patrick Ohly 4a9232d6ee autotools: avoid test == comparison
== is a bashism which fails in dash. Kudos to Jussi for reporting
the problem and the initial patch.
2011-05-16 14:43:32 +02:00
Patrick Ohly cbc02d2fc8 SQLite backend: adapted to SyncSource API changes
Failed to build because getPeerMimeType() now uses
a std::string as return type. getMimeType[Version]()
are no longer needed for sources derived from SyncSource.
2011-05-05 20:16:10 +08:00
Patrick Ohly 1e4704d8f1 autotools: use new SYNCEVOLUTION_CFLAGS/LIBS in all backends
This is primarily a cosmetic change. It may be relevant for
shared modules, because with this change they properly depend
on libsyncevolution.so.x as they should.
2010-12-02 18:53:11 +01:00
Patrick Ohly c2cc198514 enable suspend and saving blobs (MB #2425)
Implement blob support. Blobs are stored in the per-peer source
directory in a ".cache" sub-directory. We could have relied on the
peer name and device name to make file names unique when sharing
a common directory. Perhaps that is indeed the better solution:
~/.cache/syncevolution/blobs with logdir specifying the root?

Blob support is only needed when running as server. When running as
client, the binfile layer provides the necessary implementation.

Support for suspend was off by default because <resumesupport> was not
set. Adding it, because we should have whatever is
needed. <resumeitemsupport> depends on the ability to store blobs.
Not sure whether this is relevant on the client side, where these
services are provided by the binfile layer.
2010-03-18 18:37:38 +01:00
Patrick Ohly d00b622acf SQLite backend: implement m_isEmpty operation (MB #7708)
This patch demonstrates how the operation can be implemented
by sources implementing the operations themselves. The implementation
of SQLiteContactSource::isEmpty() might not be perfect, but it
should get the job done.
2010-02-19 10:00:09 +01:00
Chen Congwu 97cead8f3b Server Alerted Sync: Set Content Type in SAN
Nokia phone (S40, 7710c) does not accept the null content type.
SyncSource::getPeerMimeType is added for this purpose, each backend
implementation can provide a MimeType used for sync by default. For
TrackingSyncSource based backend implementations it maps to getMimeType
directly.
2009-11-30 11:44:38 +01:00
Patrick Ohly 5f61785608 config: reorganized for shared config layout (MB#7707)
This patch introduces code changes for the new layout without actually
using it yet. Therefore all existing tests for the older layout
still pass. The new meaning of the former "server name" is introduced:
- A plain string now refers to a peer configuration (can be client
  or server).
- The @ sign allows selecting a specific context. Different contexts
  have independent sets of local sources and peer definitions.
- An empty peer name selects a view on the configuration which contains
  no peer-specific properties. Not fully implemented yet.

The FileConfigTree is instantiated with a root which is one level high
up compare to before this patch (for example,
"~/.config/syncevolution" instead of
"./config/syncevolution/scheduleworld") and then config files include
that dropped level in their relative path name
("scheduleworld/config.ini" instead of "config.ini"). This allows
accessing the global properties in
"~/.config/syncevolution/config.ini" and will be used to move peers
further down the hierarchy
("~/.config/syncevolution/peers/scheduleworld/config.ini").

To keep the output of "--print-servers" consistent, the FileConfigTree
gets another parameter which identifies the subset of the larger tree
that is referenced by this FileConfigTree instance.

One side effect of this change is that FileConfigTree instances are no
longer completely separate. Something to keep in mind when
instantiating SyncContext multiple times (MB#8006).

Code may no longer make assumptions in which config node a property is
stored. This is determined by the new getNode() calls based on the
property attributes (hidden, sharing). The new layout is represented as
a set of config nodes. Older layouts use the same set of nodes with
identical instances assigned to them, if they don't really have separate
files for each of them.

SyncSourceNodes no longer grants direct access to the nodes, to catch
code which incorrectly access a specific node directly. For the same
reason the name of the nodes changed.

Code which needs access to all hidden or all visible properties now
does this via a config node returned by getProperties(). Currently
this is identical to the underlying nodes. Once the new layout is
active, this node will act as a multiplexer which gathers properties
from all underlying nodes when reading and picks the right one when
writing.

The "change ID" parameter for sources has been obsolete for a while
and was removed.

Reorganized the property registration so that it is a bit easier
to see which properties are hidden and which use non-default sharing.
The default sharing is "no sharing".

Some other code was improved while touching it:
- removed useless visibility[] array in favor of a i != 0 check in
  SyncConfig::copy()
- added default parameters to save/checkPassword() methods
- some constness definition changes
- Property::getProperty() is a virtual call which could only be
  overloaded in one case because the constness was wrong; now
  getProperty() always returns the string value and getPropertyValue()
  some other kind of representation of it, depending on the
  class.
- ConstSyncSourceNodes is based on SyncSourceNodes instead of duplicating
  it, which simplifies the implementation.

The simplified SyncSourceAdmin API changed slightly: instead of passing
a pointer to the source's SyncSourceNodes, the default nodes are now
found via the SyncSource pointer. For callers this is a bit less
work and it is more general.
2009-11-25 16:57:50 +01:00
Patrick Ohly 5acf33ff61 SQLite backend: added server admin calls
The recently introduced enableServerMode() and serverModeEnabled()
calls must be implemented by all sync sources not derived from
TrackingSyncSource. This patch adds the calls to the SQLiteContactSource,
using the default mechanism provided for it in SyncSourceAdmin.
2009-11-03 18:28:03 +01:00
Patrick Ohly d24a054605 fixed some compile problems
Was caused by the regular expression based search/replace done in
the previous commit. The "ENABLE_MODULES" case had some code
which broke during that commit.
2009-10-05 17:46:33 +02:00
Patrick Ohly 71fbf32c94 files and classes renamed, include statements cleaned up
The intention is to get rid of the historic and inconsistent
naming of some classes and their corresponding files:
* EvolutionSyncClient = class derived from Funambol's SyncClient,
* SyncEvolutionConfig = SyncEvolution's config

With the strict 'namespace SyncEvo' and the syncevo/ path prefix for
most header files it is no longer necessary to have "SyncEvolution" or
"Evolution" in the names. This patch thus renames as follows:
  EvolutionSyncClient => SyncContext
  EvolutionSmartPtr => SmartPtr
  SyncEvolutionCmdline => Cmdline
  SyncEvolutionConfig => SyncConfig
  SyncEvolutionUtil => util

The former EvolutionSyncClient always had a role that went beyond just
running a sync, for example it also provided config access. With the
upcoming server support it also won't be just a client. Thus the new
name "SyncContext".

The 'syncevo/' prefix is used throughout the code now.

removed whenever the prefix made it clear that the file belongs
to SyncEvolution. This helps finding incorrect include paths.

Quotes should be used exclusively for SyncEvolution files which don't
have a specific prefix yet (test.h, config.h) to help identifying
them.
2009-10-05 14:49:32 +02:00
Patrick Ohly f87ffd682d introduced "namespace SyncEvo" consistently
Added syncevo/declarations.h, which has

This is now used for all SyncEvolution source files, except
for the GTK UI, which is written in plain C. In the library
it helps to avoid name clashes.

The reason for using defines instead of spelling out "namespace SyncEvo"
is twofold:
1. if that should ever become necessary, it is easier to
   rename the namespace via configure options by changing
   the define
2. editors don't indent the whole file content
2009-10-02 17:27:45 +02:00
Patrick Ohly 7e0770461f sqlite backend: made it possible to compile out-of-tree
config.h is now only included if HAVE_CONFIG_H is set.
ENABLE_SQLITE can be set via -D instead.

Removed inclusion of "test.h" and "syncevo/Logging.h",
we don't install it.

Moved header files around so that they are only
included when needed.

Added a README with compile instructions.
2009-10-02 17:27:45 +02:00
Chen Congwu 1d368ff9a8 Enable SQlite backends (MB#5049)
SQLite backends is supported with new syncsoruce api. This demostrates how to
access synthesis data field directly.
Change tracking is done the same as TrackingSyncSource.
2009-09-29 11:38:09 +08:00
Chen Congwu 35ee636619 Dynamic loadable backends: repackage libsyncevolution to enable dynamic loadable backends
Install head files to a standard path, the remaining dependencies are
synthesis and boost
client-test is portable when ENABLE_MODULES is defined, no longer link to
backends libraries.

Add --enable-developer-mode, in which mode the backend scan path will be
under current build directory for development purposes.
2009-09-23 07:35:25 +02:00
Patrick Ohly d5961f8d8f redesigned SyncSource base class + API
The main motivation for this change is that it allows the implementor
of a backend to choose the implementations for the different aspects
of a datasource (change tracking, item import/export, logging, ...)
independently of each other. For example, change tracking via revision
strings can now be combined with exchanging data with the Synthesis
engine via a single string (the traditional method in SyncEvolution)
and with direct access to the Synthesis field list (now possible for
the first time).

The new backend API is based on the concept of providing
implementations for certain functionality via function objects instead
of implementing certain virtual methods. The advantage is that
implementors can define their own, custom interfaces and mix and match
implementations of the different groups of functionality.

Logging (see SyncSourceLogging in a later commit) can be done by
wrapping some arbitrary other item import/export function objects
(decorator design pattern).

The class hierarchy is now this:
- SyncSourceBase: interface for common utility code, all other
  classes are derived from it and thus can use that code
- SyncSource: base class which implements SyncSourceBase and
  hooks a datasource into the SyncEvolution core;
  its "struct Operations" holds the function objects which
  can be implemented in different ways
- TestingSyncSource: combines some of the following classes
  into an interface that is expected by the client-test
  program; backends only have to derive from (and implement this)
  if they want to use the automated testing
- TrackingSyncSource: provides the same functionality as
  before (change tracking via revision strings, item import/export
  as string) in a single interface; the description of the pure
  virtual methods are duplicated so that developers can go through
  this class and find everything they need to know to implement
  it

The following classes contain the code that was previously
found in the EvolutionSyncSource base class. Implementors
can derive from them and call the init() methods to inherit
and activate the functionality:
- SyncSourceSession: binds Synthesis session callbacks to
  virtual methods beginSync(), endSync()
- SyncSourceChanges: implements Synthesis item tracking callbacks
  with set of LUIDs that the user of the class has to fill
- SyncSourceDelete: binds Synthesis delete callback to
  virtual method
- SyncSourceRaw: read and write items in the backends format,
  used for testing and backup/restore
- SyncSourceSerialize: exchanges items with Synthesis engine
  using a string representation of the data; this is how
  EvolutionSyncSource has traditionally worked, so much of the
  same virtual methods are now in this class
- SyncSourceRevisions: utility class which does change tracking
  via some kind of "revision" string which changes each time
  an item is modified; this code was previously in the
  TrackingSyncSource
2009-08-26 15:41:51 +02:00
Zhu, Yongsheng 57916f6b92 SourceType: add a trailing '!' into source type to force to use type (Bug #2422)
When specifying a source type with a trailing '!',
syncevolution sets a force-format flag, which means
forcing to use the client's preferred format instead
giving the engine and server a choice to use it.
2009-07-07 17:26:47 +08:00
Patrick Ohly e2b5af8f14 extracting items: EvolutionMemoSource needs information about expected format
Both EvolutionSyncSource::backupData() (implemented for the memo source
in the TrackingSyncSource base class) and Client::Source::text::testImport
must dump items in the native format, the one which is used for restore
and for the test cases. See Bugzilla #3967 and #3929.

This broke during the 0.9 development cycle, but wasn't detected during
automated testing because the necessary tests weren't enabled for
the "text" source until recently.

Now createItem() is passed a hint what the desired format is. "raw"
is used for backup and testing.

Also removed obsolete exportData() call in EvolutionCalendarSource.
This was used before introducing the newer backupData() call.
2009-07-01 14:10:59 +02:00
Patrick Ohly f0115a4890 testing: fixed compile error and test driver linking with --enable-shared
Building the backend shared objects failed with --enable-shared because
then the *Register.cpp files have to be compiled into them. This used to
fail due to a missing -I path for test.h. When compiling the files for
the main executables it worked (the case without --enable-shared) because
those executables had the right search path.

After fixing that, it was necessary to add the *Register.cpp files
to client-test (and only that) because only that way are the tests in them
really enabled.

To avoid the resulting link errors, client-test (and again, only that)
links to the backend shared objects directly. The main executables don't
do that because then they work even if a backend cannot be loaded dynamically
due to missing dependencies (as on older Maemo without libecal). For testing
these hard dependencies are acceptable.
2009-05-12 17:23:21 +02:00
Patrick Ohly 3217ac8630 license: merged LGPL v2.1 -> LGPL v2.1 + v3 change
Followed the license change applied to the upstream content
and applied the same LGPL v2.1 + v3 license to content
created at Intel.
2009-04-30 18:35:56 +02:00
Patrick Ohly d5778457da license: changed to LGPL v2.1 + v3
As with the previous change from GPL to LGPL v2.1, this is covered
by copyright ownership and/or the Funambol contributor agreement.

The motivation for adding v3 is greater flexibility regarding
reusing the code in other places and relicensing it.

Also, now the license and disclaimer are mentioned at the start
of the source files. That is not strictly necessary, but more
explicit.
2009-04-30 18:14:03 +02:00
Patrick Ohly 25a8502a4b copyright updated
update-copyright.sh can be used to add copyright remarks for the current
year. It finds the authors who made a change in each file and adds/updates
their copyright remark. Intel employees are grouped under "Intel Corporation".
2009-03-25 15:21:04 +01:00
Patrick Ohly d71a296525 dist: fixed distcheck, bundle Synthesis
When --with-synthesis-src is used, then the Synthesis source code is compiled
automatically and included in a .tar.gz.

Added some missing EXTRA_DIST files.
2009-03-25 14:43:44 +01:00
Patrick Ohly afe23abf9d testing: fixed compilation of Evolution and SQLite sources 2009-03-25 14:43:37 +01:00
Patrick Ohly 43f2c5fec5 removed all references to Funambol header files and definitions 2009-03-25 14:43:35 +01:00
Patrick Ohly d801974f74 logging: replaced Funambol logging with our own infrastructure
Currently EvolutionSyncClient writes the following information:
- SyncEvolution messages via Logging.h to stdout if level higher
  than configured log level
- SyncEvolution messages into client.log: all messages if file
  is enabled (logdir setting)
- Synthesis global log (if logdir enabled)
- Synthesis session log (if logdir enabled)
- Synthesis message dump (if logdir enabled)
2009-03-25 14:43:34 +01:00
Patrick Ohly 3bfecb13a6 compilation fix: glib dependency in header file 2009-03-25 14:43:32 +01:00
Patrick Ohly 6f729542e6 license: changed to LGPL v2.1
This relicensing is possible despite the Funambol Contributor
Agreement (included as 'doc/Sync4jContribution.pdf') under which the
0.7 release was contributed to Funambol because among the broad range
of rights granted back by that agreement is the right to sublicense.

Furthermore, Patrick Ohly is still the copyright holder because
that moral right is not transferable in Germany.
2009-03-25 14:42:43 +01:00
Patrick Ohly 5fa63c04a6 license and copyright clarification
The source was always meant to be GPL v2 or later, but wasn't marked
consistently as such. Copyright belongs to Patrick Ohly, with all
code up to 0.7 also owned by Funambol due to a copyright transfer
at that time.


git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@739 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-26 17:45:28 +00:00
Patrick Ohly 46d72adbe0 avoid libsqlite dependency if it is not needed
git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@730 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-17 09:26:01 +00:00
Patrick Ohly 2cba0334de EvolutionSyncSource::sleepSinceModification(): delay after a sync only if needed, must be requested by sources
git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@724 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-16 07:39:08 +00:00
Patrick Ohly c4c3b72430 only include integration tests in anonymous namespace because CPPUnit unit tests become ugly otherwise
git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@702 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-03 20:06:13 +00:00
Patrick Ohly c9c8967950 use anonymous namespaces to avoid name conflicts between classes
git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@697 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-03 13:20:06 +00:00
Patrick Ohly d08d1f7e88 added automatic registration of test
git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@692 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-08-02 15:46:23 +00:00
Patrick Ohly 63e14ddc9c file layout restructuring, take II
Added missing files, finished automatic registration
of backends, Emacs mode for renamed files.


git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@685 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-07-30 17:30:10 +00:00
Patrick Ohly 5e8fa6eb94 major restructuring of the file layout and autotools build
This change makes it possible to add a new backend without
changing any of the core files. It also gets rid of some
hacks (like -export-dynamic for the binary) by putting all
core SyncEvolution code into a library.

The transition is not quite complete: there are still
some lists of existing backends, which will be removed
soon. EvolutionSmartPtr.h and EvolutionSyncSource depend
on Evolution/GNOME libs, which forces all backends to
use the right -I flags.


git-svn-id: https://zeitsenke.de/svn/SyncEvolution/trunk@684 15ad00c4-1369-45f4-8270-35d70d36bdcd
2008-07-29 21:06:11 +00:00