[DEV] import first version 1.1a

This commit is contained in:
Edouard DUPIN 2015-11-10 12:54:06 +01:00
commit 389dae52b6
57 changed files with 11097 additions and 0 deletions

20
AUTHORS Normal file
View File

@ -0,0 +1,20 @@
Author
======
Benjamin Zores <ben@geexbox.org>
With many contributions from :
- Alexis Saettler <asbin@asbin.org>
- Amir Shalem <amir@boom.org.il>
- Rémi Turboult for some UPnP standard compliance check.
- Bernd Loske for some buffer overrun fix.
- Eric Tanguy for Fedora RPM build and startup scripts.
- Julien Lincy for a better UPnP CDS respect and
commercial UPnP players compliance.
- Jonathan (no_dice at users.sourceforge.net) for large files support.
- Mostafa Hosseini <mostafah@axentra.com> for XboX 360 compliance, several UPnP compliance fixes and basic 'Search' support.
- Phil Chandler for RedBlack balanced tree sorting algorithm and some memory optimizations.
- Navaho Gunleg <navahogunleg@gmail.com> for MacOSX build fix.
- Sven Almgren <sven@tras.se> for Telnet Control interface.
Please send remarks, questions, bug reports to <ushare@geexbox.org>.

340
COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

198
ChangeLog Normal file
View File

@ -0,0 +1,198 @@
2007-12-09 Benjamin Zores <ben@geexbox.org>
* new public release (Version 1.1a).
2007-12-04 Benjamin Zores <ben@geexbox.org>
* new public release (Version 1.1).
2007-11-04 Benjamin Zores <ben@geexbox.org>
* much complete DLNA support through libdlna.
2007-07-30 Benjamin Zores <ben@geexbox.org>
* MacOSX build fix.
2007-07-05 Alexis Saettler <asbin@asbin.org>
* new public release (Version 1.0).
2007-06-26 Benjamin Zores <ben@geexbox.org>
* fix for XboX 360 file discovery (patch by Keith Brindley).
2007-06-25 Benjamin Zores <ben@geexbox.org>
* added support for DLNA (Digital Living Network Alliance),
the wonderful closed-source marketing UPnP++ specification.
uShare is now compliant with PlayStation 3 UPnP/DLNA Media Renderer.
2007-02-26 Alexis Saettler <asbin@asbin.org>
* new public release (Version 0.9.10).
* fixed http 404 error which prevented ushare 0.9.9 release to work.
2007-02-25 Alexis Saettler <asbin@asbin.org>
* new public release (Version 0.9.9).
* fixed bug with localization, use now --localedir configure's
option to set location of .mo files.
2007-01-23 Benjamin Zores <ben@geexbox.org>
* make libupnp >= 1.4.2 a requirement.
2007-01-20 Benjamin Zores <ben@geexbox.org>
* added support for FreeBSD.
2007-01-19 Benjamin Zores <ben@geexbox.org>
* use a RedBlack balanced tree searching and sorting algorithm
instead of the recursive one. It avoids recursive calls, takes
less memory (perfect for embedded systems) and executes in more
"constant time" than the logarithmic style.
Original patch by Phil Chandler.
* massive memory optimization (uShare now takes 75% less RAM ;-)
only not using fixed size URL allocation buffer (a 16000+ files
collection now only takes 5MB where it used to take 19MB).
* improved search parsing and matching mechanism (patch by Mostafa
Hosseini). XboX360 support is now complete.
Please note that to stream video to the XboX, the console should
be running dashboard version 2.0.4552.0 and only unprotected
.wmv files are supported.
2007-01-14 Benjamin Zores <ben@geexbox.org>
* make libupnp >= 1.4.1 a requirement.
* fixed support for 2GB+ files (see http://sourceforge.net/tracker/index.php?func=detail&aid=1634927&group_id=151880&atid=782410)
* fixed support for XboX 360 (thanks to Mostafa Hosseini).
* added basic support for 'Search' action.
2006-12-10 Alexis Saettler <asbin@asbin.org>
* new public release (Version 0.9.8).
2006-10-19 Alexis Saettler <asbin@asbin.org>
* add German translation (thanks to Robin H.).
* fix filtering and browse capabilities : now uShare is conform to
ContentDirectory:1 Service Template Standard.
2006-09-17 Alexis Saettler <asbin@asbin.org>
* add iso and bin mime types.
* convert special xml characters to xml (like & => &amp;)
(reported by Frank Scholz).
2006-09-10 Alexis Saettler <asbin@asbin.org>
* Use sysconfdir (from configure) to set /etc destination
* update init script to use lsb functions
2006-03-12 Benjamin Zores <ben@geexbox.org>
* new public release (Version 0.9.7)
2006-03-07 Benjamin Zores <ben@geexbox.org>
* modify configure.ac to check for functions in libupnp that
only comes with releases >= 1.3.1
(previous versions of the library are buggy and should not be used).
* added new Microsoft Registrar service which fakes the authorization
required to be able to stream to MS compliant Media Renderers
(i.e now usable with XboX 360 and probably others).
2006-02-27 Benjamin Zores <ben@geexbox.org>
* applied suggestions from Julien Lincy
* UPnP CDS compliance fix when request count and index are 0.
* removed the ':' characters from UDN anming convention (devices like
the Roku SoundBridge M2000 Network Music Player do not support it).
2006-02-19 Benjamin Zores <ben@geexbox.org>
* new public release (Version 0.9.6)
2006-01-06 Benjamin Zores <ben@geexbox.org>
* added Fedora RPM build file (ushare.spec) and startup script.
(patch by Eric Tanguy <eric.tanguy@univ-nantes.fr>)
2005-12-28 Benjamin Zores <ben@geexbox.org>
* can now specify on which port the HTTP server has to run.
(useful for security and firewall issues).
2005-12-27 Benjamin Zores <ben@geexbox.org>
* fixed a potential buffer overrun when building DIDL messages.
(patch by Bernd Loske <bernd@loskeonline.net> ).
2005-12-19 Benjamin Zores <ben@geexbox.org>
* new public release (Version 0.9.5)
2005-12-17 Benjamin Zores <ben@geexbox.org>
* added UPnP presentation URL support.
(you can now update or add contents through a web interface).
2005-12-10 Benjamin Zores <ben@geexbox.org>
* added support for subtitle file formats.
* added support for DVD specific file formats.
2005-12-08 Benjamin Zores <ben@geexbox.org>
* new public bug-fix release (Version 0.9.4)
* fixed some memleaks.
* avoid having empty shared directory name
(crash with djmount, thanks to Remi Turboult for reporting it).
* fixed handling of non-absolute content directories paths.
2005-11-23 Alexis Saettler <asbin@asbin.org>
* add logging with syslog support, in daemon mode.
2005-11-20 Benjamin Zores <ben@geexbox.org>
* new public bug-fix release (Version 0.9.3)
* Added verbose mode.
* Read configuration from /etc/ushare.conf configuration file.
* Support for daemon mode.
* Better MIME types handling.
* Support for new multimedia files extensions.
* Rewrite of some string buffer and integer manipulation tools.
2005-11-16 Alexis Saettler <asbin@asbin.org>
* modify -c parameter habdle, to allow multiple shared directories.
2005-11-13 Benjamin Zores <ben@geexbox.org>
* new public bug-fix release (Version 0.9.2)
2005-11-13 Benjamin Zores <ben@geexbox.org>
* improved UPnP compliance with Browse RequestedCount flag.
* fixed some UPnP Object ID sent in Browse messages.
2005-11-10 Benjamin Zores <ben@geexbox.org>
* new public release (Version 0.9.1)
2005-11-08 Alexis Saettler <asbin@asbin.org>
* Prepare uShare for internationalization
* Add French localization
* Add support of iconv, for UTF-8 filenames conversions
2005-10-29 Alexis Saettler <asbin@asbin.org>
* Added Debian package building scripts
2005-10-25 Benjamin Zores <ben@geexbox.org>
* First public release (Version 0.9.0)

60
Makefile Normal file
View File

@ -0,0 +1,60 @@
ifeq (,$(wildcard config.mak))
$(error "config.mak is not present, run configure !")
endif
include config.mak
DISTFILE = ushare-$(VERSION).tar.bz2
EXTRADIST = AUTHORS \
ChangeLog \
configure \
COPYING \
NEWS \
README \
THANKS \
TODO \
SUBDIRS = po \
scripts \
src \
all:
for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir $@; \
done
clean:
for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir $@; \
done
distclean: clean
for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir $@; \
done
-$(RM) -f config.log
-$(RM) -f config.mak
-$(RM) -f config.h
install:
for subdir in $(SUBDIRS); do \
$(MAKE) -C $$subdir $@; \
done
.PHONY: clean distclean install
dist:
-$(RM) $(DISTFILE)
dist=$(shell pwd)/ushare-$(VERSION) && \
for subdir in . $(SUBDIRS); do \
mkdir -p "$$dist/$$subdir"; \
$(MAKE) -C $$subdir dist-all DIST="$$dist/$$subdir"; \
done && \
tar cjf $(DISTFILE) ushare-$(VERSION)
-$(RM) -rf ushare-$(VERSION)
dist-all:
cp $(EXTRADIST) $(SRCS) Makefile $(DIST)
.PHONY: dist dist-all

108
NEWS Normal file
View File

@ -0,0 +1,108 @@
2007-12-09: GeeXboX uShare 1.1a released.
Support for XboX 360 dashboard Fall Update (.avi and .divx now are supported)
DLNA support is not enabled by default, as not mandatory.
Some configure script fixes and better support for cross-compilation.
Fixed network interface discovery on MacOSX.
2007-12-04: GeeXboX uShare 1.1 released.
Much more complete DLNA support through external libdlna.
Telnet Control interface.
Support for FLAC and HDMOV files.
FreeBSD 64bit and MacOSX build fix.
Newly written configure script.
2007-07-05 : GeeXboX uShare 1.0 released.
Fixed XboX 360 file discovery (it finally should work).
Support for Sony PlayStation 3.
DLNA compliance.
2007-02-26 : GeeXboX uShare 0.9.10 released.
Fixed http 404 error which prevented ushare 0.9.9 release to work.
2007-02-25 : GeeXboX uShare 0.9.9 released.
Added support for FreeBSD.
Use a RedBlack balanced tree searching and sorting algorithm
instead of the recursive one. It avoids recursive calls, takes
less memory (perfect for embedded systems) and executes in more
"constant time" than the logarithmic style.
Original patch by Phil Chandler.
Massive memory optimization (uShare now takes 75% less RAM ;-)
only not using fixed size URL allocation buffer (a 16000+ files
collection now only takes 5MB where it used to take 19MB).
Improved search parsing and matching mechanism (patch by Mostafa
Hosseini). XboX360 support is now complete.
Please note that to stream video to the XboX, the console should
be running dashboard version 2.0.4552.0 and only unprotected
.wmv files are supported.
Fixed support for 2GB+ files (see http://sourceforge.net/tracker/index.php?func=detail&aid=1634927&group_id=151880&atid=782410)
Fixed support for XboX 360 (thanks to Mostafa Hosseini).
Added basic support for 'Search' action.
Fixed bug with localization, use now --localedir configure's
option to set location of .mo files.
Make libupnp >= 1.4.2 a requirement.
2006-12-10 : GeeXboX uShare 0.9.8 released.
Added German translation.
Convert special xml characters to xml (like & => &amp;).
Added iso and bin mime types.
Better filtering and browse capabilities: now uShare is conform to
ContentDirectory:1 Service Template Standard.
Updated init script to use lsb functions.
2006-03-12 : GeeXboX uShare 0.9.7 released.
Added new Microsoft Registrar service which fakes the authorization
required to be able to stream to MS compliant Media Renderers.
Updated AVI file MIME type for better compliance with UPnP devices.
Better UPnP CDS compliance when request count and index are 0.
Removed the nasty characters from UDN naming for compatibility issues.
Force usage of libupnp >= 1.3.1
(previous versions of the library are buggy and should not be used).
Support for XboX 360, D-Link DSM-320 and Roku SoundBridge M2000
Network Music Player devices.
2006-02-19 : GeeXboX uShare 0.9.6 released.
Added Fedora RPM build and startup init scripts.
Support for for large files (2GB+ files).
Can now specify on which port the HTTP server has to run.
Fixed a potential buffer overrun when building DIDL messages.
2006-01-31 : uShare has been included in the Fedora Project.
See http://fedoraproject.org/extras/4/i386/repodata/repoview/ushare-0-0.9.5-4.fc4.html
2005-12-19 : GeeXboX uShare 0.9.5 released.
Added UPnP presentation URL support (you can now update or add/remove
contents through a web interface).
Added support for subtitle file formats.
Added support for DVD specific file formats.
2005-12-07 : GeeXboX uShare 0.9.4 released.
Fixed some memleaks.
Avoid having empty shared directory name.
Fixed handling of non-absolute content directories paths.
Added logging with syslog support, in daemon mode.
2005-11-20: GeeXboX uShare 0.9.3 released.
Support for multiple directories to be shared.
Added start/stop script.
Added verbose mode.
Read configuration from /etc/ushare.conf configuration file.
Support for daemon mode.
Better MIME types handling.
Support for new multimedia files extensions.
Rewrite of some string buffer and integer manipulation tools.
2005-11-13: GeeXboX uShare 0.9.2 released.
Improved UPnP compliance with Browse RequestedCount flag.
Fixed some UPnP Object ID sent in Browse messages (terrible mistake).
2005-11-10: GeeXboX uShare 0.9.1 released.
uShare ready for internationalization.
Added French localization.
Support of iconv, for UTF-8 filenames conversions.
Added Debian package building scripts.
2005-10-25: GeeXboX uShare 0.9.0 released.
First public release.
Tested with :
- Intel Digital Media Adaptor
- Intel UPnP AV Media Controller

209
README Normal file
View File

@ -0,0 +1,209 @@
GeeXboX uShare - Introduction
=============================
GeeXboX uShare is a UPnP (TM) A/V Media Server. It implements the server
component that provides UPnP media devices with information on available
multimedia files. uShare uses the built-in http server of libupnp to
stream the files to clients.
GeeXboX uShare is able to provide access to both images, videos, music
or playlists files (see below for a complete file format support list).
It does not act as an UPnP Media Adaptor and thus, can't transcode
streams to fit the client requirements.
uShare is written in C for the GeeXboX project (see http://www.geexbox.org/).
It is designed to provide access to multimedia contents to GeeXboX but can of
course be used by any other UPnP client device.
It should compile and run on any modern POSIX compatible system such as Linux.
GeeXboX uShare is free software - it is licensed under the terms of the GNU
General Public License (GPL).
Copyright and License
=====================
GeeXboX uShare is copyright (C) 2005-2007 Benjamin Zores.
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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
Note that uShare links with libupnp, which is licensed under the
terms of a modified BSD license (i.e. the original BSD license without the
advertising clause). This license is compatible with the GNU GPL.
Homepage
========
Web site and file area for uShare is hosted on GeeXboX server :
http://ushare.geexbox.org/
The latest version of uShare should always be available on this site.
Requirements
============
The following programs are required to build uShare:
* GNU C Compiler (gcc), 2.95 or later.
The GNU C Compiler is part of the GNU Compiler Collection which can be
downloaded from http://gcc.gnu.org/.
* pkg-config.
pkg-config is a helper tool used when compiling applications and libraries.
It helps you insert the correct compiler options on the command line.
(see http://pkg-config.freedesktop.org/wiki/ ).
The following UPnP library is required to build and run uShare :
* Linux SDK for UPnP Devices (libupnp), 1.4.2 or later
The libupnp library is used to communicate using the UPnP protocol.
libupnp can be downloaded from http://pupnp.sourceforge.net/.
The following DLNA library is required for proper DLNA support :
* libdlna, 0.2.1 or later
The libdlna library is used to provides DLNA profiles informations.
libdlna can be downloaded from http://libdlna.geexbox.org/.
Building and Installation
=========================
Compile uShare by running configure and then make. This should
produce an executable ushare in the src subdirectory, which can be
used right away. No extra files need to be installed.
You can pass the CFLAGS you want to configure including -DDEBUG in order
to activate support for debug messages in uShare.
Example :
CFLAGS="-Os" ./configure --prefix=/usr
make
You can enable DLNA support by doing a:
./configure --enable-dlna
If you want to install uShare on your system, run :
make install-strip
This will copy the executable and manual page into their appropriate
directories (/usr/bin and /usr/man/man1 in this example).
For more information regarding configure and make, see the INSTALL document.
Usage
=====
uShare runs from the console only. It supports the usual --help option
which displays usage and option information.
Usage: ushare [-n name] [-i interface] [-c directory] [[-c directory] ...]
Options:
-n, --name=NAME Set UPnP Friendly Name (default is 'uShare')
-i, --interface=IFACE Use IFACE Network Interface (default is 'eth0')
-f, --cfg=FILE Config file to be used
-p, --port=PORT Forces the HTTP server to run on PORT
-q, --telnet-port=PORT Forces the TELNET server to run on PORT
-c, --content=DIR Share the content of DIR directory.
-w, --no-web Disable the control web page (enabled by default)
-t, --no-telnet Disable the TELNET control (enabled by default)
-o, --override-iconv-err If iconv fails parsing name, still add to media contents (hoping the renderer can handle it)
-v, --verbose Set verbose display
-x, --xbox Use XboX 360 compliant profile
-d, --dlna Use DLNA compliant profile (PlayStation3 needs this)
-D, --daemon Run as a daemon.
-V, --version Display the version of uShare and exit
-h, --help Display this help
uShare gets its configuration from the /etc/ushare.conf file.
You can force configuration options through command line.
uShare expects one or several directory argument (-c argument),
specifying where multimedia files are stored. You should probably also use
the -i option to specify which interface uShare should listen on.
ushare -c /shares
ushare -c /shares1 --content=/shares2
You can also perform remote control of uShare UPnP Media Server through its
web interface. This let you define new content locations at runtime or
update the currently shared one in case the filesystem has changed.
Just go to :
http://ip_address:port/web/ushare.html
See the manual page for more details :
man ushare
Supported File Formats List
===========================
- Video files : asf, avi, dv, divx, wmv, mjpg, mjpeg, mpeg, mpg, mpe,
mp2p, vob, mp2t, m1v, m2v, m4v, m4p, mp4ps, ts, ogm, mkv,
rmvb, mov, qt
- Audio files : aac, ac3, aif, aiff, at3p, au, snd, dts, rmi, mp1, mp2, mp3,
mp4, mpa, ogg, wav, pcm, lpcm, l16, wma, mka, ra, rm, ram
- Images files : bmp, ico, gif, jpeg, jpg, jpe, pcd, png, pnm, ppm,
qti, qtf, qtif, tif, tiff
- Playlist files : pls, m3u, asx
If you want uShare to support more file formats, simply add its properties
in the src/mime.c table. Do not forget to send a patch to update uShare.
Feedback
========
Please send bug reports, suggestions, ideas, comments or patches to :
ushare@geexbox.org
Known bugs and limitations
==========================
If you need to listen on more than one interface, you will have to start
multiple instances of the media server.
uShare keeps some information on files in memory.
If your multimedia collection is huge, this might be a problem.
Thanks
======
Many thanks to Oskar Liljeblad for its original work on GMediaServer
(which is much more functionnal in terms of UPnP A/V features that
uShare will ever be).
References
==========
Note that this list of references is not complete.
* UPnP(TM) Standards (http://www.upnp.org/standardizeddcps/default.asp)
Trademarks
==========
UPnP(TM) is a trademark of the UPnP(TM) Implementers Corporation.
-

8
THANKS Normal file
View File

@ -0,0 +1,8 @@
Many thanks to Oskar Liljeblad for its original work on GMediaServer
(which is much more functionnal in terms of UPnP A/V features
that uShare will ever be).
Special Thanks to Rémi Turboult for some UPnP standard compliance check.
Thanks to Jason Woodward for making me aware of most of
DLNA specification internals.

3
TODO Normal file
View File

@ -0,0 +1,3 @@
- change daemon running user (currently root)
- add static libupnp in sources for easier maintaining and fixes
- add authentication to web interface ??

740
configure vendored Executable file
View File

@ -0,0 +1,740 @@
#!/bin/sh
#
# uShare configure script - (c) 2007 Benjamin Zores
#
# (fully inspirated from ffmpeg configure script, thanks to Fabrice Bellard)
#
# make sure we are running under a compatible shell
unset foo
(: ${foo%%bar}) 2>/dev/null && ! (: ${foo?}) 2>/dev/null
if test "$?" != 0; then
if test "x$USHARE_CONFIGURE_EXEC" = x; then
USHARE_CONFIGURE_EXEC=1
export USHARE_CONFIGURE_EXEC
exec bash "$0" "$@"
exec ksh "$0" "$@"
exec /usr/xpg4/bin/sh "$0" "$@"
fi
echo "No compatible shell script interpreter found."
exit 1
fi
show_help(){
echo "Usage: configure [options]"
echo "Options: [defaults in brackets after descriptions]"
echo
echo "Standard options:"
echo " --help print this message"
echo " --log[=FILE|yes|no] log tests and output to FILE [config.log]"
echo " --prefix=PREFIX install in PREFIX [$PREFIX]"
echo " --bindir=DIR install binaries in DIR [PREFIX/bin]"
echo " --sysconfdir=DIR configuration files DIR [PREFIX/etc]"
echo " --localedir=DIR use locales from DIR [PREFIX/share/locale]"
echo ""
echo "Extended options:"
echo " --enable-dlna enable DLNA support through libldna"
echo " --disable-dlna disable DLNA support"
echo " --disable-nls do not use Native Language Support"
echo ""
echo "Search paths:"
echo " --with-libupnp-dir=DIR check for libupnp installed in DIR"
echo " --with-libdlna-dir=DIR check for libdlna installed in DIR"
echo ""
echo "Advanced options (experts only):"
echo " --enable-debug enable debugging symbols"
echo " --disable-debug disable debugging symbols"
echo " --disable-strip disable stripping of executables at installation"
echo " --disable-optimize disable compiler optimization"
echo " --cross-prefix=PREFIX use PREFIX for compilation tools [$cross_prefix]"
echo " --cross-compile assume a cross-compiler is used"
exit 1
}
log(){
echo "$@" >>$logfile
}
log_file(){
log BEGIN $1
cat -n $1 >>$logfile
log END $1
}
echolog(){
log "$@"
echo "$@"
}
clean(){
rm -f $TMPC $TMPO $TMPE $TMPS
}
die(){
echolog "$@"
if enabled logging; then
echo "See file \"$logfile\" produced by configure for more details."
else
echo "Rerun configure with logging enabled (do not use --log=no) for more details."
fi
clean
exit 1
}
enabled(){
eval test "x\$$1" = "xyes"
}
flags_saved(){
(: ${SAVE_CFLAGS?}) 2>/dev/null
}
save_flags(){
flags_saved && return
SAVE_CFLAGS="$CFLAGS"
SAVE_LDFLAGS="$LDFLAGS"
SAVE_extralibs="$extralibs"
}
restore_flags(){
CFLAGS="$SAVE_CFLAGS"
LDFLAGS="$SAVE_LDFLAGS"
extralibs="$SAVE_extralibs"
unset SAVE_CFLAGS
unset SAVE_LDFLAGS
unset SAVE_extralibs
}
temp_cflags(){
temp_append CFLAGS "$@"
}
temp_ldflags(){
temp_append LDFLAGS "$@"
}
temp_extralibs(){
temp_append extralibs "$@"
}
temp_append(){
local var
var=$1
shift
save_flags
append_var "$var" "$@"
}
append_var(){
local var f
var=$1
shift
for f in $@; do
if eval echo \$$var | grep -qv -e "$f"; then
eval "$var=\"\$$var $f\""
fi
done
}
append(){
local var
var=$1
shift
flags_saved && append_var "SAVE_$var" "$@"
append_var "$var" "$@"
}
add_cflags(){
append CFLAGS "$@"
}
add_ldflags(){
append LDFLAGS "$@"
}
add_extralibs(){
append extralibs "$@"
}
add_clog(){
echo "#define $1 $2" >> $CONFIG_H
}
add_clog_str(){
echo "#define $1 \"$2\"" >> $CONFIG_H
}
check_cmd(){
log "$@"
"$@" >>$logfile 2>&1
}
check_cc(){
log check_cc "$@"
cat >$TMPC
log_file $TMPC
check_cmd $cc $CFLAGS "$@" -c -o $TMPO $TMPC
}
check_cpp(){
log check_cpp "$@"
cat >$TMPC
log_file $TMPC
check_cmd $cc $CFLAGS "$@" -E -o $TMPO $TMPC
}
check_ld(){
log check_ld "$@"
check_cc || return
check_cmd $cc $LDFLAGS "$@" -o $TMPE $TMPO $extralibs
}
check_exec(){
check_ld "$@" && { enabled cross_compile || $TMPE >>$logfile 2>&1; }
}
check_cflags(){
log check_cflags "$@"
check_cc "$@" <<EOF && add_cflags "$@"
int x;
EOF
}
check_ldflags(){
log check_ldflags "$@"
check_ld "$@" <<EOF && add_ldflags "$@"
int main(){
return 0;
}
EOF
}
check_header(){
local header
log check_header "$@"
header=$1
shift
check_cpp "$@" <<EOF
#include <$header>
int x;
EOF
}
check_func(){
local func
log check_func "$@"
func=$1
shift
check_ld "$@" <<EOF
extern int $func();
int main(){
$func();
return 0;
}
EOF
}
check_lib(){
local header func err
log check_lib "$@"
header="$1"
func="$2"
shift 2
temp_extralibs "$@"
check_header $header && check_func $func && add_extralibs "$@"
err=$?
restore_flags
return $err
}
check_lib_version() {
check_cmd pkg-config "$1" --atleast-version="$2"
err=$?
return $err
}
append_config(){
echo "$@" >> $CONFIGFILE
}
expand_var(){
v="$1"
while true; do
eval t="$v"
test "$t" = "$v" && break
v="$t"
done
echo "$v"
}
# set temporary file name
if test ! -z "$TMPDIR" ; then
TMPDIR1="${TMPDIR}"
elif test ! -z "$TEMPDIR" ; then
TMPDIR1="${TEMPDIR}"
else
TMPDIR1="/tmp"
fi
TMPC="${TMPDIR1}/ushare-${RANDOM}-$$-${RANDOM}.c"
TMPO="${TMPDIR1}/ushare-${RANDOM}-$$-${RANDOM}.o"
TMPE="${TMPDIR1}/ushare-${RANDOM}-$$-${RANDOM}"
TMPS="${TMPDIR1}/ushare-${RANDOM}-$$-${RANDOM}.S"
CONFIGFILE="config.mak"
CONFIG_H="config.h"
#################################################
# set default parameters
#################################################
logging="yes"
logfile="config.log"
PREFIX="/usr/local"
bindir='${PREFIX}/bin'
sysconfdir='${PREFIX}/etc'
localedir='${PREFIX}/share/locale'
dlna="no"
nls="yes"
cc="gcc"
make="make"
strip="strip"
cpu=`uname -m`
optimize="yes"
debug="no"
dostrip="yes"
extralibs=""
installstrip="-s"
cross_compile="no"
INSTALL="/usr/bin/install -c"
VERSION="1.1a"
system_name=`uname -s 2>&1`
#################################################
# set cpu variable and specific cpu flags
#################################################
case "$cpu" in
i386|i486|i586|i686|i86pc|BePC)
cpu="x86"
;;
x86_64|amd64)
cpu="x86"
canon_arch="`$cc -dumpmachine | sed -e 's,\([^-]*\)-.*,\1,'`"
if [ x"$canon_arch" = x"x86_64" -o x"$canon_arch" = x"amd64" ]; then
if [ -z "`echo $CFLAGS | grep -- -m32`" ]; then
cpu="x86_64"
fi
fi
;;
# armv4l is a subset of armv5tel
arm|armv4l|armv5tel)
cpu="armv4l"
;;
alpha)
cpu="alpha"
;;
"Power Macintosh"|ppc|ppc64|powerpc)
cpu="powerpc"
;;
mips|mipsel|IP*)
cpu="mips"
;;
sun4u|sparc64)
cpu="sparc64"
;;
sparc)
cpu="sparc"
;;
sh4)
cpu="sh4"
;;
parisc|parisc64)
cpu="parisc"
;;
s390|s390x)
cpu="s390"
;;
m68k)
cpu="m68k"
;;
ia64)
cpu="ia64"
;;
bfin)
cpu="bfin"
;;
*)
cpu="unknown"
;;
esac
# OS test booleans functions
issystem() {
test "`echo $system_name | tr A-Z a-z`" = "`echo $1 | tr A-Z a-z`"
}
linux() { issystem "Linux" || issystem "uClinux" ; return "$?" ; }
sunos() { issystem "SunOS" ; return "$?" ; }
hpux() { issystem "HP-UX" ; return "$?" ; }
irix() { issystem "IRIX" ; return "$?" ; }
aix() { issystem "AIX" ; return "$?" ; }
cygwin() { issystem "CYGWIN" ; return "$?" ; }
freebsd() { issystem "FreeBSD" || issystem "GNU/kFreeBSD"; return "$?" ; }
netbsd() { issystem "NetBSD" ; return "$?" ; }
bsdos() { issystem "BSD/OS" ; return "$?" ; }
openbsd() { issystem "OpenBSD" ; return "$?" ; }
bsd() { freebsd || netbsd || bsdos || openbsd ; return "$?" ; }
qnx() { issystem "QNX" ; return "$?" ; }
darwin() { issystem "Darwin" ; return "$?" ; }
gnu() { issystem "GNU" ; return "$?" ; }
mingw32() { issystem "MINGW32" ; return "$?" ; }
morphos() { issystem "MorphOS" ; return "$?" ; }
amigaos() { issystem "AmigaOS" ; return "$?" ; }
win32() { cygwin || mingw32 ; return "$?" ; }
beos() { issystem "BEOS" ; return "$?" ; }
#################################################
# check options
#################################################
for opt do
optval="${opt#*=}"
case "$opt" in
--log)
;;
--log=*) logging="$optval"
;;
--prefix=*) PREFIX="$optval"; force_prefix=yes
;;
--bindir=*) bindir="$optval";
;;
--sysconfdir=*) sysconfdir="$optval";
;;
--localedir=*) localedir="$optval";
;;
--with-libupnp-dir=*) libupnpdir="$optval";
;;
--with-libdlna-dir=*) libdlnadir="$optval";
;;
--disable-nls) nls="no"
;;
--enable-dlna) dlna="yes"
;;
--disable-dlna) dlna="no"
;;
--enable-debug) debug="yes"
;;
--disable-debug) debug="no"
;;
--disable-strip) dostrip="no"
;;
--disable-optimize) optimize="no"
;;
--cross-prefix=*) cross_prefix="$optval"
;;
--cross-compile) cross_compile="yes"
;;
--help) show_help
;;
*)
echo "Unknown option \"$opt\"."
echo "See $0 --help for available options."
exit 1
;;
esac
done
if [ -n "$cross_prefix" ]; then
cross_compile="yes"
cc="${cross_prefix}${cc}"
strip="${cross_prefix}${strip}"
else
[ -n "$CC" ] && cc="$CC"
[ -n "$STRIP" ] && strip="$STRIP"
fi
[ -n "$MAKE" ] && make="$MAKE"
#################################################
# create logging file
#################################################
if test "$logging" != no; then
enabled logging || logfile="$logging"
echo "# $0 $@" >$logfile
set >>$logfile
else
logfile=/dev/null
fi
#################################################
# compiler sanity check
#################################################
echolog "Checking for compiler available..."
check_exec <<EOF
int main(){
return 0;
}
EOF
if test "$?" != 0; then
echo "$cc is unable to create an executable file."
if test -z "$cross_prefix" -a "$cross_compile" = no; then
echo "If $cc is a cross-compiler, use the --cross-compile option."
fi
die "C compiler test failed."
fi
#################################################
# check for target specific flags
#################################################
# check for SIMD availability
# AltiVec flags: The FSF version of GCC differs from the Apple version
if test $cpu = "powerpc"; then
if test $altivec = "yes"; then
if test -n "`$cc -v 2>&1 | grep version | grep Apple`"; then
add_cflags "-faltivec"
else
add_cflags "-maltivec -mabi=altivec"
fi
fi
fi
check_header altivec.h && _altivec_h=yes || _altivec_h=no
# check if our compiler supports Motorola AltiVec C API
if enabled altivec; then
if enabled _altivec_h; then
inc_altivec_h="#include <altivec.h>"
else
inc_altivec_h=
fi
check_cc <<EOF || altivec=no
$inc_altivec_h
int main(void) {
vector signed int v1, v2, v3;
v1 = vec_add(v2,v3);
return 0;
}
EOF
fi
# mmi only available on mips
if [ "$mmi" = "default" ]; then
if [ "$cpu" = "mips" ]; then
mmi="yes"
else
mmi="no"
fi
fi
# check if our compiler supports mmi
enabled mmi && check_cc <<EOF || mmi="no"
int main(void) {
__asm__ ("lq \$2, 0(\$2)");
return 0;
}
EOF
# test gcc version to see if vector builtins can be used
# currently only used on i386 for MMX builtins
check_cc -msse <<EOF && builtin_vector=yes || builtin_vector=no
#include <xmmintrin.h>
int main(void) {
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
return 0;
#else
#error no vector builtins
#endif
}
EOF
# test for mm3dnow.h
test "$cpu" = "x86_64" && march=k8 || march=athlon
check_cc -march=$march <<EOF && mm3dnow=yes || mm3dnow=no
#include <mm3dnow.h>
int main(void) {
__m64 b1;
b1 = _m_pswapd(b1);
_m_femms();
return 0;
}
EOF
# ---
# big/little-endian test
if test "$cross_compile" = "no"; then
check_ld <<EOF || die "endian test failed" && $TMPE && bigendian="yes"
#include <inttypes.h>
int main(int argc, char ** argv){
volatile uint32_t i=0x01234567;
return (*((uint8_t*)(&i))) == 0x67;
}
EOF
else
# programs cannot be launched if cross compiling, so make a static guess
if test "$cpu" = "powerpc" -o "$cpu" = "mips" ; then
bigendian="yes"
fi
fi
# add some useful compiler flags if supported
add_cflags -I..
check_cflags -W
check_cflags -Wall
check_cflags -D_LARGEFILE_SOURCE
check_cflags -D_FILE_OFFSET_BITS=64
check_cflags -D_REENTRANT
linux && add_cflags -D_GNU_SOURCE
#################################################
# check for debug symbols
#################################################
if enabled debug; then
add_cflags -g3
add_cflags -DHAVE_DEBUG
dostrip=no
fi
if enabled optimize; then
if test -n "`$cc -v 2>&1 | grep xlc`"; then
add_cflags "-O5"
add_ldflags "-O5"
else
add_cflags "-O3"
fi
fi
#################################################
# check for locales (optional)
#################################################
echolog "Checking for locales ..."
check_header locale.h && add_cflags -DHAVE_LOCALE_H
check_lib locale.h setlocale "" && add_cflags -DHAVE_SETLOCALE
#################################################
# check for ifaddr (optional)
#################################################
echolog "Checking for ifaddrs ..."
check_lib ifaddrs.h getifaddrs "" && add_cflags -DHAVE_IFADDRS_H
#################################################
# check for langinfo (optional)
#################################################
echolog "Checking for langinfo ..."
check_header langinfo.h && add_cflags -DHAVE_LANGINFO_H
check_lib langinfo.h nl_langinfo "" && add_cflags -DHAVE_LANGINFO_CODESET
#################################################
# check for iconv (optional)
#################################################
echolog "Checking for iconv ..."
check_lib iconv.h iconv "" && add_cflags -DHAVE_ICONV
#################################################
# check for libupnp and friends (mandatory)
#################################################
if [ -n "$libupnpdir" ]; then
check_cflags -I$libupnpdir
check_ldflags -L$libupnpdir
fi
echolog "Checking for libixml ..."
check_lib upnp/ixml.h ixmlRelaxParser -lixml || die "Error, can't find libixml !"
echolog "Checking for libthreadutil ..."
check_lib upnp/ThreadPool.h ThreadPoolAdd "-lthreadutil -lpthread" || die "Error, can't find libthreadutil !"
add_extralibs -lpthread
libupnp_min_version="1.4.2"
echolog "Checking for libupnp >= $libupnp_min_version ..."
check_lib upnp/upnp.h UpnpSetMaxContentLength -lupnp || die "Error, can't find libupnp !"
check_lib_version libupnp $libupnp_min_version || die "Error, libupnp < $libupnp_min_version !"
add_cflags `pkg-config libupnp --cflags`
add_extralibs `pkg-config libupnp --libs`
#################################################
# check for libdlna (mandatory if enabled)
#################################################
if test "$dlna" = "yes"; then
libdlna_min_version="0.2.1"
echolog "Checking for libdlna >= $libdlna_min_version ..."
if [ -n "$libdlnadir" ]; then
check_cflags -I$libdlnadir
check_ldflags -L$libdlnadir
fi
check_lib dlna.h dlna_register_all_media_profiles -ldlna || die "Error, can't find libdlna (install it or use --disable-dlna) !"
check_lib_version libdlna $libdlna_min_version || die "Error, libdlna < $libdlna_min_version !"
add_cflags -DHAVE_DLNA
add_cflags `pkg-config libdlna --cflags`
add_extralibs `pkg-config libdlna --libs`
fi
#################################################
# logging result
#################################################
echolog ""
echolog "uShare: configure is OK"
echolog " version $VERSION"
echolog " using libupnp `pkg-config libupnp --modversion`"
test $dlna = yes && echolog " using libdlna `pkg-config libdlna --modversion`"
echolog "configuration:"
echolog " install prefix $PREFIX"
echolog " configuration dir $sysconfdir"
echolog " locales dir $localedir"
echolog " NLS support $nls"
echolog " DLNA support $dlna"
echolog " C compiler $cc"
echolog " STRIP $strip"
echolog " make $make"
echolog " CPU $cpu ($tune)"
echolog " debug symbols $debug"
echolog " strip symbols $dostrip"
echolog " optimize $optimize"
echolog ""
echolog " CFLAGS $CFLAGS"
echolog " LDFLAGS $LDFLAGS"
echolog " extralibs $extralibs"
echolog ""
#################################################
# save configs attributes
#################################################
echolog "Creating $CONFIGFILE ..."
echo "# Automatically generated by configure - do not modify!" > $CONFIGFILE
append_config "VERSION=$VERSION"
append_config "PREFIX=$PREFIX"
append_config "prefix=\$(DESTDIR)\$(PREFIX)"
append_config "bindir=\$(DESTDIR)$bindir"
append_config "sysconfdir=\$(DESTDIR)$sysconfdir"
append_config "localedir=\$(DESTDIR)$localedir"
append_config "MAKE=$make"
append_config "CC=$cc"
append_config "LN=ln"
if enabled dostrip; then
append_config "STRIP=$strip"
append_config "INSTALLSTRIP=$installstrip"
else
append_config "STRIP=echo ignoring strip"
append_config "INSTALLSTRIP="
fi
append_config "EXTRALIBS=$extralibs"
append_config "OPTFLAGS=$CFLAGS"
append_config "LDFLAGS=$LDFLAGS"
append_config "INSTALL=$INSTALL"
append_config "DEBUG=$debug"
echolog "Creating $CONFIG_H ..."
echo "/* Automatically generated by configure - do not modify! */" > $CONFIG_H
add_clog_str VERSION "$VERSION"
add_clog_str PACKAGE "ushare"
add_clog_str PACKAGE_NAME "uShare"
add_clog_str SYSCONFDIR `expand_var "${sysconfdir}"`
add_clog_str LOCALEDIR `expand_var "${localedir}"`
test $nls = yes && add_clog CONFIG_NLS 1
clean
exit 0

89
po/Makefile Normal file
View File

@ -0,0 +1,89 @@
ifeq (,$(wildcard ../config.mak))
$(error "../config.mak is not present, run configure !")
endif
include ../config.mak
top_srcdir = ..
DOMAIN = ushare
# Languages to generate
LANGS = fr de
GMSGFMT = /usr/bin/msgfmt
XGETTEXT = /usr/bin/xgettext
MSGMERGE = msgmerge
MSGMERGE_UPDATE = /usr/bin/msgmerge --update
MSGINIT = msginit
MSGCONV = msgconv
MSGFILTER = msgfilter
EXTRADIST = $(POFILES) $(GMOFILES) \
POTFILES \
$(DOMAIN).pot \
GMOFILES = $(addsuffix .gmo,$(LANGS))
POFILES = $(addsuffix .po,$(LANGS))
POTFILES = $(shell for f in `cat POTFILES`; do echo -n "$(top_srcdir)/$$f "; done)
all: stamp-po
stamp-po: $(DOMAIN).pot
$(MAKE) $(GMOFILES)
touch $@
.po.gmo:
$(GMSGFMT) -c --statistics -o $@ $<
.SUFFIXES: .po .gmo
$(POFILES): $(DOMAIN).pot
$(MSGMERGE_UPDATE) $@ $(DOMAIN).pot;
update-po:
$(MAKE) $(DOMAIN).pot-update
$(MAKE) update-gmo
update-gmo: $(GMOFILES)
@:
.PHONY: update-po update-gmo
# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
$(DOMAIN).pot-update: $(POTFILES) POTFILES
$(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
--add-comments=TRANSLATORS: \
--keyword=_ --keyword=N_ \
--files-from=POTFILES \
--copyright-holder='http://ushare.geexbox.org' \
--msgid-bugs-address="ushare@geexbox.org" \
-o $(DOMAIN).pot
# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
# every "make" invocation, only create it when it is missing.
# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
$(DOMAIN).pot:
$(MAKE) $(DOMAIN).pot-update
clean:
-$(RM) -f stamp-po
distclean: clean
-$(RM) -f $(GMOFILES)
install: $(GMOFILES)
for gmo in $(GMOFILES); do \
lang=`echo $$gmo | sed -e 's/\.gmo$$//'`; \
$(INSTALL) -d $(localedir)/$$lang/LC_MESSAGES; \
$(INSTALL) -m 644 $$lang.gmo $(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo; \
done
dist-all: $(GMOFILES)
cp $(EXTRADIST) $(SRCS) Makefile $(DIST)
.PHONY: dist-all install clean distclean

4
po/POTFILES Normal file
View File

@ -0,0 +1,4 @@
src/ushare.c
src/metadata.c
src/cfgparser.c
src/presentation.c

BIN
po/de.gmo Normal file

Binary file not shown.

308
po/de.po Normal file
View File

@ -0,0 +1,308 @@
# German translations for uShare package.
# Copyright (C) 2006 http://ushare.geexbox.org
# This file is distributed under the same license as the uShare package.
# uShare Team <ushare@geexbox.org>, 2006.
#
msgid ""
msgstr ""
"Project-Id-Version: uShare 0.9.8\n"
"Report-Msgid-Bugs-To: ushare@geexbox.org\n"
"POT-Creation-Date: 2007-11-18 16:25+0100\n"
"PO-Revision-Date: 2006-09-20 22:12+0200\n"
"Last-Translator: German uShare Team <ushare@geexbox.org>\n"
"Language-Team: German uShare Team <ushare@geexbox.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/ushare.c:263
msgid "Stopping UPnP Service ...\n"
msgstr "UPnP Service wird gestoppt ...\n"
#: src/ushare.c:316
msgid "Initializing UPnP subsystem ...\n"
msgstr "Initialisiere UPnP Subsystem ...\n"
#: src/ushare.c:320
msgid "Cannot initialize UPnP subsystem\n"
msgstr "Kann UPnP Subsystem nicht initialisieren\n"
#: src/ushare.c:325
msgid "Could not set Max content UPnP\n"
msgstr ""
#: src/ushare.c:328
msgid "Starting in XboX 360 compliant profile ...\n"
msgstr ""
#: src/ushare.c:333
msgid "Starting in DLNA compliant profile ...\n"
msgstr ""
#: src/ushare.c:342
#, c-format
msgid "UPnP MediaServer listening on %s:%d\n"
msgstr "UPnP MediaServer lauscht auf %s:%d\n"
#: src/ushare.c:350
msgid "Cannot set virtual directory callbacks\n"
msgstr "Kann virtuelle Verzeichnis-Callbacks nicht setzen\n"
#: src/ushare.c:358
msgid "Cannot add virtual directory for web server\n"
msgstr "Kann virtuelles Verzeichnis für den Webserver nicht zurücksetzen\n"
#: src/ushare.c:368 src/ushare.c:386
msgid "Cannot register UPnP device\n"
msgstr "Kann Upnp Gerät nicht registrieren\n"
#: src/ushare.c:376
msgid "Cannot unregister UPnP device\n"
msgstr "Kann UpnP Gerät nicht deregistrieren\n"
#: src/ushare.c:391
msgid "Sending UPnP advertisement for device ...\n"
msgstr "Sende UpnP Advertisement für Gerät...\n"
#: src/ushare.c:394
msgid "Listening for control point connections ...\n"
msgstr "Lausche auf Control Point Verbindungen...\n"
#: src/ushare.c:450
#, c-format
msgid "Interface %s is down.\n"
msgstr "Interface %s ist down.\n"
#: src/ushare.c:451 src/ushare.c:462
msgid "Recheck uShare's configuration and try again !\n"
msgstr "Überprüfe uShare's Konfiguration und versuche es erneut!\n"
#: src/ushare.c:461
#, c-format
msgid "Can't find interface %s.\n"
msgstr "Kann Interface %s nicht finden.\n"
#: src/ushare.c:622
msgid "Reloading configuration...\n"
msgstr "Lade Konfiguration erneut...\n"
#: src/ushare.c:683 src/ushare.c:771
msgid "Error: no content directory to be shared.\n"
msgstr "Fehler: Kein Content Verzeichnis zum sharen.\n"
#: src/ushare.c:691
#, c-format
msgid "%s (version %s), a lightweight UPnP Media Server.\n"
msgstr "%s (Version %s), ein leichtgewichtiger UPnP Media Server.\n"
#: src/ushare.c:693
#, c-format
msgid "Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n"
msgstr "Benjamin Zores (C) 2005-2007, für das GeeXboX Team.\n"
#: src/ushare.c:694
#, c-format
msgid "See http://ushare.geexbox.org/ for updates.\n"
msgstr "Für Updates gehe auf http://ushare.geexbox.org/.\n"
#: src/ushare.c:711
msgid ""
"Server is shutting down: other clients will be notified soon, Bye bye ...\n"
msgstr ""
#: src/ushare.c:746
#, c-format
msgid "Warning: can't parse file \"%s\".\n"
msgstr "Warnung: Kann Datei nicht parsen \"%s\".\n"
#: src/ushare.c:802
#, c-format
msgid "Error: failed to daemonize program : %s\n"
msgstr "Fehler: Daemonisierung des Programms nicht möglich: %s\n"
#: src/ushare.c:825
msgid "Terminates the uShare server"
msgstr ""
#: src/metadata.c:402
msgid "Failed to add the RB lookup tree\n"
msgstr ""
#: src/metadata.c:530
msgid "Cannot create RB tree for lookups\n"
msgstr ""
#: src/metadata.c:537
msgid "Building Metadata List ...\n"
msgstr "Erzeuge Metadata List ...\n"
#: src/metadata.c:550
#, c-format
msgid "Looking for files in content directory : %s\n"
msgstr "Suche nach Dateien im Content Verzeichnis : %s\n"
#: src/metadata.c:574
#, c-format
msgid "Found %d files and subdirectories.\n"
msgstr "Fand %d Dateien und Unterverzeichnisse.\n"
#: src/cfgparser.c:166
#, c-format
msgid "Warning: port doesn't fit IANA port assignements.\n"
msgstr "Warnung: Port passt nicht zur IANA Port Zuordnung.\n"
#: src/cfgparser.c:168
#, c-format
msgid ""
"Warning: Only Dynamic or Private Ports can be used (from 49152 through "
"65535)\n"
msgstr ""
"Warnung: Nur dynamische und private Ports können benutzt werden (von 49152 "
"bis 65535)\n"
#: src/cfgparser.c:318
#, c-format
msgid ""
"Usage: ushare [-n name] [-i interface] [-p port] [-c directory] [[-c "
"directory]...]\n"
msgstr ""
"Usage: ushare [-n Name] [-i Interface] [-p Port] [-c Verzeichnis] [[-c "
"Verzeichnis]...]\n"
#: src/cfgparser.c:319
#, c-format
msgid "Options:\n"
msgstr "Optionen:\n"
#: src/cfgparser.c:320
#, c-format
msgid " -n, --name=NAME\tSet UPnP Friendly Name (default is '%s')\n"
msgstr " -n, --name=NAME\tSet UPnP-freundlicher Name (vorgegeben ist '%s')\n"
#: src/cfgparser.c:322
#, c-format
msgid " -i, --interface=IFACE\tUse IFACE Network Interface (default is '%s')\n"
msgstr ""
" -i, --interface=IFACE\tBenutze IFACE Netzwerk Interface (vorgegeben ist '%"
"s')\n"
#: src/cfgparser.c:324
#, c-format
msgid " -f, --cfg=FILE\t\tConfig file to be used\n"
msgstr ""
#: src/cfgparser.c:325
#, c-format
msgid " -p, --port=PORT\tForces the HTTP server to run on PORT\n"
msgstr ""
" -p, --port=PORT\tLegt das PORT fest, auf dem der HTTP Server laufen soll\n"
#: src/cfgparser.c:326
#, fuzzy, c-format
msgid " -q, --telnet-port=PORT\tForces the TELNET server to run on PORT\n"
msgstr ""
" -p, --port=PORT\tLegt das PORT fest, auf dem der HTTP Server laufen soll\n"
#: src/cfgparser.c:327
#, c-format
msgid " -c, --content=DIR\tShare the content of DIR directory\n"
msgstr ""
" -c, --content=DIR\tShare den Inhalt des folgenden Content Verzeichnisses"
"(DIR)\n"
#: src/cfgparser.c:328
#, c-format
msgid " -w, --no-web\t\tDisable the control web page (enabled by default)\n"
msgstr ""
" -w, --no-web\t\tDeaktiviere die Kontrollwebseite (Vorgabe: Webseite ist "
"aktiviert)\n"
#: src/cfgparser.c:329
#, fuzzy, c-format
msgid " -t, --no-telnet\tDisable the TELNET control (enabled by default)\n"
msgstr ""
" -w, --no-web\t\tDeaktiviere die Kontrollwebseite (Vorgabe: Webseite ist "
"aktiviert)\n"
#: src/cfgparser.c:330
#, c-format
msgid ""
" -o, --override-iconv-err\tIf iconv fails parsing name, still add to media "
"contents (hoping the renderer can handle it)\n"
msgstr ""
#: src/cfgparser.c:331
#, c-format
msgid " -v, --verbose\t\tSet verbose display\n"
msgstr " -v, --verbose\t\tSetze Verbose Display\n"
#: src/cfgparser.c:332
#, c-format
msgid " -x, --xbox\t\tUse XboX 360 compliant profile\n"
msgstr ""
#: src/cfgparser.c:334
#, c-format
msgid " -d, --dlna\t\tUse DLNA compliant profile (PlayStation3 needs this)\n"
msgstr ""
#: src/cfgparser.c:336
#, c-format
msgid " -D, --daemon\t\tRun as a daemon\n"
msgstr " -D, --daemon\t\tAusführen des Porgramms in Daemon Mode\n"
#: src/cfgparser.c:337
#, c-format
msgid " -V, --version\t\tDisplay the version of uShare and exit\n"
msgstr ""
" -V, --version\t\tZeigt die uShare Version an und beendet uShare danach\n"
#: src/cfgparser.c:338
#, c-format
msgid " -h, --help\t\tDisplay this help\n"
msgstr " -h, --help\t\tZeigt diese Hilfe an\n"
#: src/presentation.c:108 src/presentation.c:143
msgid "uShare Information Page"
msgstr "uShare Informationsseite"
#: src/presentation.c:155
msgid "uShare UPnP A/V Media Server"
msgstr "uShare UPnP A/V Media Server"
#: src/presentation.c:156
msgid "Information Page"
msgstr "Informationsseite"
#: src/presentation.c:163
msgid "Version"
msgstr "Version"
#: src/presentation.c:166
msgid "Device UDN"
msgstr "Gerät UDN"
#: src/presentation.c:168
msgid "Number of shared files and directories"
msgstr "Anzahl der geshareten Dateien und Verzeichnisse"
#: src/presentation.c:178
msgid "Share"
msgstr "Share"
#: src/presentation.c:184
msgid "unShare!"
msgstr "unShare!"
#: src/presentation.c:190
msgid "Add a new share : "
msgstr "Füge neues Share hinzu : "
#: src/presentation.c:196
msgid "Share!"
msgstr "Share!"
#: src/presentation.c:207
msgid "Refresh Shares ..."
msgstr "Erneuere Shares ..."

BIN
po/fr.gmo Normal file

Binary file not shown.

307
po/fr.po Normal file
View File

@ -0,0 +1,307 @@
# French translations for uShare package.
# Copyright (C) 2005-2007 http://ushare.geexbox.org
# This file is distributed under the same license as the ushare package.
# Alexis Saettler <asbin@asbin.org>, 2005-2007.
#
msgid ""
msgstr ""
"Project-Id-Version: uShare 1.0\n"
"Report-Msgid-Bugs-To: ushare@geexbox.org\n"
"POT-Creation-Date: 2007-11-18 16:25+0100\n"
"PO-Revision-Date: 2007-07-05 20:55+0200\n"
"Last-Translator: Alexis Saettler <asbin@asbin.org>\n"
"Language-Team: uShare French trad <ushare@geexbox.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: src/ushare.c:263
msgid "Stopping UPnP Service ...\n"
msgstr "Arrêt du service UPnP ...\n"
#: src/ushare.c:316
msgid "Initializing UPnP subsystem ...\n"
msgstr "Initialisation du système UPnP ...\n"
#: src/ushare.c:320
msgid "Cannot initialize UPnP subsystem\n"
msgstr "Impossible d'initialiser le système UPnP ...\n"
#: src/ushare.c:325
msgid "Could not set Max content UPnP\n"
msgstr "Impossible de configurer la taille max pour UPnP\n"
#: src/ushare.c:328
msgid "Starting in XboX 360 compliant profile ...\n"
msgstr "Démarre en mode compatibilité pour XboX 360 ...\n"
#: src/ushare.c:333
msgid "Starting in DLNA compliant profile ...\n"
msgstr "Démarre en mode compatibilité DLNA ...\n"
#: src/ushare.c:342
#, c-format
msgid "UPnP MediaServer listening on %s:%d\n"
msgstr "Serveur Multimédia UPnP en écoute sur %s:%d\n"
#: src/ushare.c:350
msgid "Cannot set virtual directory callbacks\n"
msgstr "Impossible de créer les retours des dossiers virtuels\n"
#: src/ushare.c:358
msgid "Cannot add virtual directory for web server\n"
msgstr "Impossible d'ajouter le dossier virtuel du serveur web\n"
#: src/ushare.c:368 src/ushare.c:386
msgid "Cannot register UPnP device\n"
msgstr "Impossible d'enregistrer le périphérique UPnP\n"
#: src/ushare.c:376
msgid "Cannot unregister UPnP device\n"
msgstr "Impossible de supprimer le périphérique UPnP\n"
#: src/ushare.c:391
msgid "Sending UPnP advertisement for device ...\n"
msgstr "Envoit des informations UPnP du périphérique ...\n"
#: src/ushare.c:394
msgid "Listening for control point connections ...\n"
msgstr "Attente de connexions ...\n"
#: src/ushare.c:450
#, c-format
msgid "Interface %s is down.\n"
msgstr "L'interface %s est arrêtée.\n"
#: src/ushare.c:451 src/ushare.c:462
msgid "Recheck uShare's configuration and try again !\n"
msgstr "Revérifiez la configuration de uShare et recommencez !\n"
#: src/ushare.c:461
#, c-format
msgid "Can't find interface %s.\n"
msgstr "Impossible de trouver l'interface %s.\n"
#: src/ushare.c:622
msgid "Reloading configuration...\n"
msgstr "Rechargement de la configuration...\n"
#: src/ushare.c:683 src/ushare.c:771
msgid "Error: no content directory to be shared.\n"
msgstr "Erreur : aucun répertoire de contenus à partager.\n"
#: src/ushare.c:691
#, c-format
msgid "%s (version %s), a lightweight UPnP Media Server.\n"
msgstr "%s (version %s), un serveur mutlimédia UPnP léger.\n"
#: src/ushare.c:693
#, c-format
msgid "Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n"
msgstr "Benjamin Zores (C) 2005-2007, pour l'équipe GeeXboX.\n"
#: src/ushare.c:694
#, c-format
msgid "See http://ushare.geexbox.org/ for updates.\n"
msgstr "Voir http://ushare.geexbox.org/ pour les mises à jour.\n"
#: src/ushare.c:711
msgid ""
"Server is shutting down: other clients will be notified soon, Bye bye ...\n"
msgstr ""
"Arrêt du serveur en cours : les autres clients seront notifié bientôt. Au "
"revoir !\n"
#: src/ushare.c:746
#, c-format
msgid "Warning: can't parse file \"%s\".\n"
msgstr "Attention : impossible de lire le fichier \"%s\".\n"
#: src/ushare.c:802
#, c-format
msgid "Error: failed to daemonize program : %s\n"
msgstr "Erreur : impossible de démoniser le programme : %s\n"
#: src/ushare.c:825
msgid "Terminates the uShare server"
msgstr "Arrêt du serveur uShare"
#: src/metadata.c:402
msgid "Failed to add the RB lookup tree\n"
msgstr "Impossible d'ajouter l'arbre de recherche RB\n"
#: src/metadata.c:530
msgid "Cannot create RB tree for lookups\n"
msgstr "Impossible de créer l'arbre RB pour les recherches\n"
#: src/metadata.c:537
msgid "Building Metadata List ...\n"
msgstr "Création de la liste de données\n"
#: src/metadata.c:550
#, c-format
msgid "Looking for files in content directory : %s\n"
msgstr "Recherche des fichiers dans le répertoire de contenus : %s\n"
#: src/metadata.c:574
#, c-format
msgid "Found %d files and subdirectories.\n"
msgstr "%d fichiers et sous-répertoires trouvés.\n"
#: src/cfgparser.c:166
#, c-format
msgid "Warning: port doesn't fit IANA port assignements.\n"
msgstr "Attention : ce port ne suit pas les recommendations IANA.\n"
#: src/cfgparser.c:168
#, c-format
msgid ""
"Warning: Only Dynamic or Private Ports can be used (from 49152 through "
"65535)\n"
msgstr ""
"Attention : seuls les ports dynamiques ou privés peuvent être utilisés (de "
"49152 à 65535)\n"
#: src/cfgparser.c:318
#, c-format
msgid ""
"Usage: ushare [-n name] [-i interface] [-p port] [-c directory] [[-c "
"directory]...]\n"
msgstr ""
"Usage : ushare [-n nom] [-i interface] [-p port] [-c répertoire] [[-c "
"répertoire]...]\n"
#: src/cfgparser.c:319
#, c-format
msgid "Options:\n"
msgstr "Options :\n"
#: src/cfgparser.c:320
#, c-format
msgid " -n, --name=NAME\tSet UPnP Friendly Name (default is '%s')\n"
msgstr " -n, --name=NOM\t\tDéfini le nom UPnP ('%s' par défaut)\n"
#: src/cfgparser.c:322
#, c-format
msgid " -i, --interface=IFACE\tUse IFACE Network Interface (default is '%s')\n"
msgstr ""
" -i, --interface=IFACE\tUtiliser l'interface réseau IFACE ('%s' par défaut)\n"
#: src/cfgparser.c:324
#, c-format
msgid " -f, --cfg=FILE\t\tConfig file to be used\n"
msgstr " -f, --cfg=FICHIER\tFichier de configuration à utiliser\n"
#: src/cfgparser.c:325
#, c-format
msgid " -p, --port=PORT\tForces the HTTP server to run on PORT\n"
msgstr " -p, --port=PORT\tForce le serveur HTTP à utiliser ce PORT\n"
#: src/cfgparser.c:326
#, c-format
msgid " -q, --telnet-port=PORT\tForces the TELNET server to run on PORT\n"
msgstr " -q, --telnet-port=PORT\tForce le server TELNET à utiliser ce PORT\n"
#: src/cfgparser.c:327
#, c-format
msgid " -c, --content=DIR\tShare the content of DIR directory\n"
msgstr " -c, --content=REP\tPartage le contenu du répertoire REP\n"
#: src/cfgparser.c:328
#, c-format
msgid " -w, --no-web\t\tDisable the control web page (enabled by default)\n"
msgstr ""
" -w, --no-web\t\tDésactive la page de contrôle web (activée par défaut)\n"
#: src/cfgparser.c:329
#, c-format
msgid " -t, --no-telnet\tDisable the TELNET control (enabled by default)\n"
msgstr ""
" -t, --no-telnet\tDésactiver le contrôle par TELNET (activé par défaut)\n"
#: src/cfgparser.c:330
#, c-format
msgid ""
" -o, --override-iconv-err\tIf iconv fails parsing name, still add to media "
"contents (hoping the renderer can handle it)\n"
msgstr ""
" -o, --override-iconv-err\tSi iconv n'arrive pas à traduire le nom du "
"fichier, ajouter tout de même le contenu (en espérant que le lecteur puisse "
"le lire)\n"
#: src/cfgparser.c:331
#, c-format
msgid " -v, --verbose\t\tSet verbose display\n"
msgstr " -v, --verbose\t\tMode bavard\n"
#: src/cfgparser.c:332
#, c-format
msgid " -x, --xbox\t\tUse XboX 360 compliant profile\n"
msgstr " -x, --xbox\t\tUtiliser le mode de compatibilité XboX 360\n"
#: src/cfgparser.c:334
#, c-format
msgid " -d, --dlna\t\tUse DLNA compliant profile (PlayStation3 needs this)\n"
msgstr ""
" -d, --dlna\t\tUtiliser le mode de compatibilité DLNA pour PlayStation3\n"
#: src/cfgparser.c:336
#, c-format
msgid " -D, --daemon\t\tRun as a daemon\n"
msgstr " -D, --daemon\t\tDémarrer en mode démon\n"
#: src/cfgparser.c:337
#, c-format
msgid " -V, --version\t\tDisplay the version of uShare and exit\n"
msgstr ""
" -V, --version\t\tAffiche la version de uShare et quitte le programme\n"
#: src/cfgparser.c:338
#, c-format
msgid " -h, --help\t\tDisplay this help\n"
msgstr " -h, --help\t\tAffiche cette aide\n"
#: src/presentation.c:108 src/presentation.c:143
msgid "uShare Information Page"
msgstr "Page d'information de uShare"
#: src/presentation.c:155
msgid "uShare UPnP A/V Media Server"
msgstr "uShare - Serveur Multimédia A/V UPnP"
#: src/presentation.c:156
msgid "Information Page"
msgstr "Page d'information"
#: src/presentation.c:163
msgid "Version"
msgstr "Version"
#: src/presentation.c:166
msgid "Device UDN"
msgstr "Périphérique UDN"
#: src/presentation.c:168
msgid "Number of shared files and directories"
msgstr "Nombre de fichiers et sous-répertoires partagés"
#: src/presentation.c:178
msgid "Share"
msgstr "Partage"
#: src/presentation.c:184
msgid "unShare!"
msgstr "Départager !"
#: src/presentation.c:190
msgid "Add a new share : "
msgstr "Ajouter un partage : "
#: src/presentation.c:196
msgid "Share!"
msgstr "Partager !"
#: src/presentation.c:207
msgid "Refresh Shares ..."
msgstr "Rafraîchir les partages ..."

293
po/ushare.pot Normal file
View File

@ -0,0 +1,293 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR http://ushare.geexbox.org
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: ushare@geexbox.org\n"
"POT-Creation-Date: 2007-11-18 17:38+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: src/ushare.c:263
msgid "Stopping UPnP Service ...\n"
msgstr ""
#: src/ushare.c:316
msgid "Initializing UPnP subsystem ...\n"
msgstr ""
#: src/ushare.c:320
msgid "Cannot initialize UPnP subsystem\n"
msgstr ""
#: src/ushare.c:325
msgid "Could not set Max content UPnP\n"
msgstr ""
#: src/ushare.c:328
msgid "Starting in XboX 360 compliant profile ...\n"
msgstr ""
#: src/ushare.c:333
msgid "Starting in DLNA compliant profile ...\n"
msgstr ""
#: src/ushare.c:342
#, c-format
msgid "UPnP MediaServer listening on %s:%d\n"
msgstr ""
#: src/ushare.c:350
msgid "Cannot set virtual directory callbacks\n"
msgstr ""
#: src/ushare.c:358
msgid "Cannot add virtual directory for web server\n"
msgstr ""
#: src/ushare.c:368 src/ushare.c:386
msgid "Cannot register UPnP device\n"
msgstr ""
#: src/ushare.c:376
msgid "Cannot unregister UPnP device\n"
msgstr ""
#: src/ushare.c:391
msgid "Sending UPnP advertisement for device ...\n"
msgstr ""
#: src/ushare.c:394
msgid "Listening for control point connections ...\n"
msgstr ""
#: src/ushare.c:450
#, c-format
msgid "Interface %s is down.\n"
msgstr ""
#: src/ushare.c:451 src/ushare.c:462
msgid "Recheck uShare's configuration and try again !\n"
msgstr ""
#: src/ushare.c:461
#, c-format
msgid "Can't find interface %s.\n"
msgstr ""
#: src/ushare.c:622
msgid "Reloading configuration...\n"
msgstr ""
#: src/ushare.c:683 src/ushare.c:771
msgid "Error: no content directory to be shared.\n"
msgstr ""
#: src/ushare.c:691
#, c-format
msgid "%s (version %s), a lightweight UPnP Media Server.\n"
msgstr ""
#: src/ushare.c:693
#, c-format
msgid "Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n"
msgstr ""
#: src/ushare.c:694
#, c-format
msgid "See http://ushare.geexbox.org/ for updates.\n"
msgstr ""
#: src/ushare.c:711
msgid ""
"Server is shutting down: other clients will be notified soon, Bye bye ...\n"
msgstr ""
#: src/ushare.c:746
#, c-format
msgid "Warning: can't parse file \"%s\".\n"
msgstr ""
#: src/ushare.c:802
#, c-format
msgid "Error: failed to daemonize program : %s\n"
msgstr ""
#: src/ushare.c:825
msgid "Terminates the uShare server"
msgstr ""
#: src/metadata.c:402
msgid "Failed to add the RB lookup tree\n"
msgstr ""
#: src/metadata.c:530
msgid "Cannot create RB tree for lookups\n"
msgstr ""
#: src/metadata.c:537
msgid "Building Metadata List ...\n"
msgstr ""
#: src/metadata.c:550
#, c-format
msgid "Looking for files in content directory : %s\n"
msgstr ""
#: src/metadata.c:574
#, c-format
msgid "Found %d files and subdirectories.\n"
msgstr ""
#: src/cfgparser.c:166
#, c-format
msgid "Warning: port doesn't fit IANA port assignements.\n"
msgstr ""
#: src/cfgparser.c:168
#, c-format
msgid ""
"Warning: Only Dynamic or Private Ports can be used (from 49152 through "
"65535)\n"
msgstr ""
#: src/cfgparser.c:318
#, c-format
msgid ""
"Usage: ushare [-n name] [-i interface] [-p port] [-c directory] [[-c "
"directory]...]\n"
msgstr ""
#: src/cfgparser.c:319
#, c-format
msgid "Options:\n"
msgstr ""
#: src/cfgparser.c:320
#, c-format
msgid " -n, --name=NAME\tSet UPnP Friendly Name (default is '%s')\n"
msgstr ""
#: src/cfgparser.c:322
#, c-format
msgid " -i, --interface=IFACE\tUse IFACE Network Interface (default is '%s')\n"
msgstr ""
#: src/cfgparser.c:324
#, c-format
msgid " -f, --cfg=FILE\t\tConfig file to be used\n"
msgstr ""
#: src/cfgparser.c:325
#, c-format
msgid " -p, --port=PORT\tForces the HTTP server to run on PORT\n"
msgstr ""
#: src/cfgparser.c:326
#, c-format
msgid " -q, --telnet-port=PORT\tForces the TELNET server to run on PORT\n"
msgstr ""
#: src/cfgparser.c:327
#, c-format
msgid " -c, --content=DIR\tShare the content of DIR directory\n"
msgstr ""
#: src/cfgparser.c:328
#, c-format
msgid " -w, --no-web\t\tDisable the control web page (enabled by default)\n"
msgstr ""
#: src/cfgparser.c:329
#, c-format
msgid " -t, --no-telnet\tDisable the TELNET control (enabled by default)\n"
msgstr ""
#: src/cfgparser.c:330
#, c-format
msgid ""
" -o, --override-iconv-err\tIf iconv fails parsing name, still add to media "
"contents (hoping the renderer can handle it)\n"
msgstr ""
#: src/cfgparser.c:331
#, c-format
msgid " -v, --verbose\t\tSet verbose display\n"
msgstr ""
#: src/cfgparser.c:332
#, c-format
msgid " -x, --xbox\t\tUse XboX 360 compliant profile\n"
msgstr ""
#: src/cfgparser.c:334
#, c-format
msgid " -d, --dlna\t\tUse DLNA compliant profile (PlayStation3 needs this)\n"
msgstr ""
#: src/cfgparser.c:336
#, c-format
msgid " -D, --daemon\t\tRun as a daemon\n"
msgstr ""
#: src/cfgparser.c:337
#, c-format
msgid " -V, --version\t\tDisplay the version of uShare and exit\n"
msgstr ""
#: src/cfgparser.c:338
#, c-format
msgid " -h, --help\t\tDisplay this help\n"
msgstr ""
#: src/presentation.c:108 src/presentation.c:143
msgid "uShare Information Page"
msgstr ""
#: src/presentation.c:155
msgid "uShare UPnP A/V Media Server"
msgstr ""
#: src/presentation.c:156
msgid "Information Page"
msgstr ""
#: src/presentation.c:163
msgid "Version"
msgstr ""
#: src/presentation.c:166
msgid "Device UDN"
msgstr ""
#: src/presentation.c:168
msgid "Number of shared files and directories"
msgstr ""
#: src/presentation.c:178
msgid "Share"
msgstr ""
#: src/presentation.c:184
msgid "unShare!"
msgstr ""
#: src/presentation.c:190
msgid "Add a new share : "
msgstr ""
#: src/presentation.c:196
msgid "Share!"
msgstr ""
#: src/presentation.c:207
msgid "Refresh Shares ..."
msgstr ""

26
scripts/Makefile Normal file
View File

@ -0,0 +1,26 @@
ifeq (,$(wildcard ../config.mak))
$(error "config.mak is not present, run configure !")
endif
include ../config.mak
CONF_FILE = "ushare.conf"
INITD_FILE = "ushare"
EXTRADIST = $(CONF_FILE) $(INITD_FILE)
all:
clean:
distclean:
install:
$(INSTALL) -d $(sysconfdir)
$(INSTALL) -m 644 $(CONF_FILE) $(sysconfdir)
$(INSTALL) -d $(sysconfdir)/init.d
$(INSTALL) -m 755 $(INITD_FILE) $(sysconfdir)/init.d
dist-all:
cp $(EXTRADIST) $(SRCS) Makefile $(DIST)
.PHONY: clean disctlean install dist-all

82
scripts/ushare Executable file
View File

@ -0,0 +1,82 @@
#!/bin/sh -e
#
# uShare init script
#
### BEGIN INIT INFO
# Provides: ushare
# Required-Start: $local_fs $syslog $network
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: uShare
# Description: uShare UPnP (TM) A/V & DLNA Media Server
# You should edit configuration in /etc/ushare.conf file
# See http://ushare.geexbox.org for details
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/ushare
NAME=ushare
DESC="uShare UPnP A/V & DLNA Media Server"
PIDFILE=/var/run/ushare.pid
CONFIGFILE=/etc/ushare.conf
# abort if no executable exists
[ -x $DAEMON ] || exit 0
# Get lsb functions
. /lib/lsb/init-functions
. /etc/default/rcS
[ -f /etc/default/ushare ] && . /etc/default/ushare
checkpid() {
[ -e $PIDFILE ] || touch $PIDFILE
}
check_shares() {
if [ -r "$CONFIGFILE" ]; then
. $CONFIGFILE
[ -n "$USHARE_DIR" ] && return 0
fi
return 1
}
case "$1" in
start)
log_daemon_msg "Starting $DESC: $NAME"
if ! $(check_shares); then
log_warning_msg "No shares avalaible ..."
log_end_msg 0
else
checkpid
start-stop-daemon --start --quiet --background --oknodo \
--make-pidfile --pidfile $PIDFILE \
--exec $DAEMON -- $USHARE_OPTIONS
log_end_msg $?
fi
;;
stop)
log_daemon_msg "Stopping $DESC: $NAME"
start-stop-daemon --stop --signal 2 --quiet --oknodo --pidfile $PIDFILE
log_end_msg $?
;;
reload|force-reload)
log_daemon_msg "Reloading $DESC: $NAME"
start-stop-daemon --stop --signal 1 --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
log_end_msg $?
;;
restart)
$0 stop
$0 start
;;
*)
N=/etc/init.d/$NAME
log_success_msg "Usage: $N {start|stop|restart|reload|force-reload}"
exit 1
;;
esac
exit 0

45
scripts/ushare.conf Normal file
View File

@ -0,0 +1,45 @@
# /etc/ushare.conf
# Configuration file for uShare
# uShare UPnP Friendly Name (default is 'uShare').
USHARE_NAME=
# Interface to listen to (default is eth0).
# Ex : USHARE_IFACE=eth1
USHARE_IFACE=
# Port to listen to (default is random from IANA Dynamic Ports range)
# Ex : USHARE_PORT=49200
USHARE_PORT=
# Port to listen for Telnet connections
# Ex : USHARE_TELNET_PORT=1337
USHARE_TELNET_PORT=
# Directories to be shared (space or CSV list).
# Ex: USHARE_DIR=/dir1,/dir2
USHARE_DIR=
# Use to override what happens when iconv fails to parse a file name.
# The default uShare behaviour is to not add the entry in the media list
# This option overrides that behaviour and adds the non-iconv'ed string into
# the media list, with the assumption that the renderer will be able to
# handle it. Devices like Noxon 2 have no problem with strings being passed
# as is. (Umlauts for all!)
#
# Options are TRUE/YES/1 for override and anything else for default behaviour
USHARE_OVERRIDE_ICONV_ERR=
# Enable Web interface (yes/no)
ENABLE_WEB=
# Enable Telnet control interface (yes/no)
ENABLE_TELNET=
# Use XboX 360 compatibility mode (yes/no)
ENABLE_XBOX=
# Use DLNA profile (yes/no)
# This is needed for PlayStation3 to work (among other devices)
ENABLE_DLNA=

87
src/Makefile Normal file
View File

@ -0,0 +1,87 @@
ifeq (,$(wildcard ../config.mak))
$(error "../config.mak is not present, run configure !")
endif
include ../config.mak
PROG = ushare
EXTRADIST = ushare.1 \
cds.h \
cms.h \
msr.h \
http.h \
presentation.h \
metadata.h \
mime.h \
services.h \
buffer.h \
util_iconv.h \
content.h \
cfgparser.h \
trace.h \
redblack.h \
osdep.h \
ctrl_telnet.h \
ushare.h \
gettext.h \
minmax.h \
SRCS = \
cds.c \
cms.c \
msr.c \
http.c \
presentation.c \
metadata.c \
mime.c \
services.c \
buffer.c \
util_iconv.c \
content.c \
cfgparser.c \
trace.c \
redblack.c \
osdep.c \
ctrl_telnet.c \
ushare.c
OBJS = $(SRCS:.c=.o)
.SUFFIXES: .c .o
all: depend $(PROG)
.c.o:
$(CC) -c $(CFLAGS) $(OPTFLAGS) -o $@ $<
$(PROG): $(OBJS)
$(CC) $(OBJS) $(LDFLAGS) $(EXTRALIBS) -o $@
clean:
-$(RM) -f *.o $(PROG)
-$(RM) -f .depend
distclean:
install: $(PROG)
$(INSTALL) -d $(bindir)
$(INSTALL) $(PROG) $(bindir)
$(STRIP) $(INSTALLSTRIP) $(bindir)/$(PROG)
depend:
$(CC) -I.. -MM $(CFLAGS) $(SRCS) 1>.depend
.PHONY: clean distclean install depend
dist-all:
cp $(EXTRADIST) $(SRCS) Makefile $(DIST)
.PHONY: dist-all
#
# include dependency files if they exist
#
ifneq ($(wildcard .depend),)
include .depend
endif

106
src/buffer.c Normal file
View File

@ -0,0 +1,106 @@
/* buffer.c - String buffer manipulation tools.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "buffer.h"
#include "minmax.h"
#define BUFFER_DEFAULT_CAPACITY 32768
struct buffer_t *
buffer_new (void)
{
struct buffer_t *buffer = NULL;
buffer = (struct buffer_t *) malloc (sizeof (struct buffer_t));
if (!buffer)
return NULL;
buffer->buf = NULL;
buffer->len = 0;
buffer->capacity = 0;
return buffer;
}
void
buffer_append (struct buffer_t *buffer, const char *str)
{
size_t len;
if (!buffer || !str)
return;
if (!buffer->buf)
{
buffer->capacity = BUFFER_DEFAULT_CAPACITY;
buffer->buf = (char *) malloc (buffer->capacity * sizeof (char));
memset (buffer->buf, '\0', buffer->capacity);
}
len = buffer->len + strlen (str);
if (len >= buffer->capacity)
{
buffer->capacity = MAX (len + 1, 2 * buffer->capacity);
buffer->buf = realloc (buffer->buf, buffer->capacity);
}
strcat (buffer->buf, str);
buffer->len += strlen (str);
}
void
buffer_appendf (struct buffer_t *buffer, const char *format, ...)
{
char str[BUFFER_DEFAULT_CAPACITY];
int size;
va_list va;
if (!buffer || !format)
return;
va_start (va, format);
size = vsnprintf (str, BUFFER_DEFAULT_CAPACITY, format, va);
if (size >= BUFFER_DEFAULT_CAPACITY)
{
char* dynstr = (char *) malloc (size + 1);
vsnprintf (dynstr, size + 1, format, va);
buffer_append (buffer, dynstr);
free (dynstr);
}
else
buffer_append (buffer, str);
va_end (va);
}
void
buffer_free (struct buffer_t *buffer)
{
if (!buffer)
return;
if (buffer->buf)
free (buffer->buf);
free (buffer);
}

38
src/buffer.h Normal file
View File

@ -0,0 +1,38 @@
/* buffer.h - String buffer manipulation tools header.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _STRING_BUFFER_H_
#define _STRING_BUFFER_H_
struct buffer_t {
char *buf;
size_t len;
size_t capacity;
};
struct buffer_t *buffer_new (void)
__attribute__ ((malloc));
void buffer_free (struct buffer_t *buffer);
void buffer_append (struct buffer_t *buffer, const char *str);
void buffer_appendf (struct buffer_t *buffer, const char *format, ...)
__attribute__ ((format (printf , 2, 3)));
#endif /* _STRING_BUFFER_H_ */

872
src/cds.c Normal file
View File

@ -0,0 +1,872 @@
/*
* cds.c : GeeXboX uShare Content Directory Service
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "ushare.h"
#include "services.h"
#include "ushare.h"
#include "services.h"
#include "metadata.h"
#include "mime.h"
#include "buffer.h"
#include "minmax.h"
/* Represent the CDS GetSearchCapabilities action. */
#define SERVICE_CDS_ACTION_SEARCH_CAPS "GetSearchCapabilities"
/* Represent the CDS GetSortCapabilities action. */
#define SERVICE_CDS_ACTION_SORT_CAPS "GetSortCapabilities"
/* Represent the CDS GetSystemUpdateID action. */
#define SERVICE_CDS_ACTION_UPDATE_ID "GetSystemUpdateID"
/* Represent the CDS Browse action. */
#define SERVICE_CDS_ACTION_BROWSE "Browse"
/* Represent the CDS Search action. */
#define SERVICE_CDS_ACTION_SEARCH "Search"
/* Represent the CDS SearchCaps argument. */
#define SERVICE_CDS_ARG_SEARCH_CAPS "SearchCaps"
/* Represent the CDS SortCaps argument. */
#define SERVICE_CDS_ARG_SORT_CAPS "SortCaps"
/* Represent the CDS UpdateId argument. */
#define SERVICE_CDS_ARG_UPDATE_ID "Id"
/* Represent the CDS StartingIndex argument. */
#define SERVICE_CDS_ARG_START_INDEX "StartingIndex"
/* Represent the CDS RequestedCount argument. */
#define SERVICE_CDS_ARG_REQUEST_COUNT "RequestedCount"
/* Represent the CDS ObjectID argument. */
#define SERVICE_CDS_ARG_OBJECT_ID "ObjectID"
/* Represent the CDS Filter argument. */
#define SERVICE_CDS_ARG_FILTER "Filter"
/* Represent the CDS BrowseFlag argument. */
#define SERVICE_CDS_ARG_BROWSE_FLAG "BrowseFlag"
/* Represent the CDS SortCriteria argument. */
#define SERVICE_CDS_ARG_SORT_CRIT "SortCriteria"
/* Represent the CDS SearchCriteria argument. */
#define SERVICE_CDS_ARG_SEARCH_CRIT "SearchCriteria"
/* Represent the CDS Root Object ID argument. */
#define SERVICE_CDS_ROOT_OBJECT_ID "0"
/* Represent the CDS DIDL Message Metadata Browse flag argument. */
#define SERVICE_CDS_BROWSE_METADATA "BrowseMetadata"
/* Represent the CDS DIDL Message DirectChildren Browse flag argument. */
#define SERVICE_CDS_BROWSE_CHILDREN "BrowseDirectChildren"
/* Represent the CDS DIDL Message Result argument. */
#define SERVICE_CDS_DIDL_RESULT "Result"
/* Represent the CDS DIDL Message NumberReturned argument. */
#define SERVICE_CDS_DIDL_NUM_RETURNED "NumberReturned"
/* Represent the CDS DIDL Message TotalMatches argument. */
#define SERVICE_CDS_DIDL_TOTAL_MATCH "TotalMatches"
/* Represent the CDS DIDL Message UpdateID argument. */
#define SERVICE_CDS_DIDL_UPDATE_ID "UpdateID"
/* DIDL parameters */
/* Represent the CDS DIDL Message Header Namespace. */
#define DIDL_NAMESPACE \
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " \
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " \
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
/* Represent the CDS DIDL Message Header Tag. */
#define DIDL_LITE "DIDL-Lite"
/* Represent the CDS DIDL Message Item value. */
#define DIDL_ITEM "item"
/* Represent the CDS DIDL Message Item ID value. */
#define DIDL_ITEM_ID "id"
/* Represent the CDS DIDL Message Item Parent ID value. */
#define DIDL_ITEM_PARENT_ID "parentID"
/* Represent the CDS DIDL Message Item Restricted value. */
#define DIDL_ITEM_RESTRICTED "restricted"
/* Represent the CDS DIDL Message Item UPnP Class value. */
#define DIDL_ITEM_CLASS "upnp:class"
/* Represent the CDS DIDL Message Item Title value. */
#define DIDL_ITEM_TITLE "dc:title"
/* Represent the CDS DIDL Message Item Resource value. */
#define DIDL_RES "res"
/* Represent the CDS DIDL Message Item Protocol Info value. */
#define DIDL_RES_INFO "protocolInfo"
/* Represent the CDS DIDL Message Item Resource Size value. */
#define DIDL_RES_SIZE "size"
/* Represent the CDS DIDL Message Container value. */
#define DIDL_CONTAINER "container"
/* Represent the CDS DIDL Message Container ID value. */
#define DIDL_CONTAINER_ID "id"
/* Represent the CDS DIDL Message Container Parent ID value. */
#define DIDL_CONTAINER_PARENT_ID "parentID"
/* Represent the CDS DIDL Message Container number of children value. */
#define DIDL_CONTAINER_CHILDS "childCount"
/* Represent the CDS DIDL Message Container Restricted value. */
#define DIDL_CONTAINER_RESTRICTED "restricted"
/* Represent the CDS DIDL Message Container Searchable value. */
#define DIDL_CONTAINER_SEARCH "searchable"
/* Represent the CDS DIDL Message Container UPnP Class value. */
#define DIDL_CONTAINER_CLASS "upnp:class"
/* Represent the CDS DIDL Message Container Title value. */
#define DIDL_CONTAINER_TITLE "dc:title"
/* Represent the "upnp:class" reserved keyword for Search action */
#define SEARCH_CLASS_MATCH_KEYWORD "(upnp:class = \""
/* Represent the "upnp:class derived from" reserved keyword */
#define SEARCH_CLASS_DERIVED_KEYWORD "(upnp:class derivedfrom \""
/* Represent the "res@protocolInfo contains" reserved keyword */
#define SEARCH_PROTOCOL_CONTAINS_KEYWORD "(res@protocolInfo contains \""
/* Represent the Search default keyword */
#define SEARCH_OBJECT_KEYWORD "object"
/* Represent the Search 'AND' connector keyword */
#define SEARCH_AND ") and ("
static bool
filter_has_val (const char *filter, const char *val)
{
char *x = NULL, *token = NULL;
char *m_buffer = NULL, *buffer;
int len = strlen (val);
bool ret = false;
if (!strcmp (filter, "*"))
return true;
x = strdup (filter);
if (x)
{
m_buffer = (char*) malloc (strlen (x));
if (m_buffer)
{
buffer = m_buffer;
token = strtok_r (x, ",", &buffer);
while (token)
{
if (*val == '@')
token = strchr (token, '@');
if (token && !strncmp (token, val, len))
{
ret = true;
break;
}
token = strtok_r (NULL, ",", &buffer);
}
free (m_buffer);
}
free (x);
}
return ret;
}
/* UPnP ContentDirectory Service actions */
static bool
cds_get_search_capabilities (struct action_event_t *event)
{
upnp_add_response (event, SERVICE_CDS_ARG_SEARCH_CAPS, "");
return event->status;
}
static bool
cds_get_sort_capabilities (struct action_event_t *event)
{
upnp_add_response (event, SERVICE_CDS_ARG_SORT_CAPS, "");
return event->status;
}
static bool
cds_get_system_update_id (struct action_event_t *event)
{
upnp_add_response (event, SERVICE_CDS_ARG_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
return event->status;
}
static void
didl_add_header (struct buffer_t *out)
{
buffer_appendf (out, "<%s %s>", DIDL_LITE, DIDL_NAMESPACE);
}
static void
didl_add_footer (struct buffer_t *out)
{
buffer_appendf (out, "</%s>", DIDL_LITE);
}
static void
didl_add_tag (struct buffer_t *out, char *tag, char *value)
{
if (value)
buffer_appendf (out, "<%s>%s</%s>", tag, value, tag);
}
static void
didl_add_param (struct buffer_t *out, char *param, char *value)
{
if (value)
buffer_appendf (out, " %s=\"%s\"", param, value);
}
static void
didl_add_value (struct buffer_t *out, char *param, off_t value)
{
buffer_appendf (out, " %s=\"%lld\"", param, value);
}
static void
didl_add_item (struct buffer_t *out, int item_id,
int parent_id, char *restricted, char *class, char *title,
char *protocol_info, off_t size, char *url, char *filter)
{
buffer_appendf (out, "<%s", DIDL_ITEM);
didl_add_value (out, DIDL_ITEM_ID, item_id);
didl_add_value (out, DIDL_ITEM_PARENT_ID, parent_id);
didl_add_param (out, DIDL_ITEM_RESTRICTED, restricted);
buffer_append (out, ">");
didl_add_tag (out, DIDL_ITEM_CLASS, class);
didl_add_tag (out, DIDL_ITEM_TITLE, title);
if (filter_has_val (filter, DIDL_RES))
{
buffer_appendf (out, "<%s", DIDL_RES);
// protocolInfo is required :
didl_add_param (out, DIDL_RES_INFO, protocol_info);
if (filter_has_val (filter, "@"DIDL_RES_SIZE))
didl_add_value (out, DIDL_RES_SIZE, size);
buffer_append (out, ">");
if (url)
{
extern struct ushare_t *ut;
buffer_appendf (out, "http://%s:%d%s/%s",
UpnpGetServerIpAddress (), ut->port, VIRTUAL_DIR, url);
}
buffer_appendf (out, "</%s>", DIDL_RES);
}
buffer_appendf (out, "</%s>", DIDL_ITEM);
}
static void
didl_add_container (struct buffer_t *out, int id, int parent_id,
int child_count, char *restricted, char *searchable,
char *title, char *class)
{
buffer_appendf (out, "<%s", DIDL_CONTAINER);
didl_add_value (out, DIDL_CONTAINER_ID, id);
didl_add_value (out, DIDL_CONTAINER_PARENT_ID, parent_id);
if (child_count >= 0)
didl_add_value (out, DIDL_CONTAINER_CHILDS, child_count);
didl_add_param (out, DIDL_CONTAINER_RESTRICTED, restricted);
didl_add_param (out, DIDL_CONTAINER_SEARCH, searchable);
buffer_append (out, ">");
didl_add_tag (out, DIDL_CONTAINER_CLASS, class);
didl_add_tag (out, DIDL_CONTAINER_TITLE, title);
buffer_appendf (out, "</%s>", DIDL_CONTAINER);
}
static int
cds_browse_metadata (struct action_event_t *event, struct buffer_t *out,
int index, int count, struct upnp_entry_t *entry,
char *filter)
{
int result_count = 0, c = 0;
if (!entry)
return -1;
if (entry->child_count == -1) /* item : file */
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
entry->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, entry->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol (entry->mime_type);
didl_add_header (out);
#ifdef HAVE_DLNA
entry->dlna_profile ?
didl_add_item (out, entry->id, entry->parent
? entry->parent->id : -1, "false",
dlna_profile_upnp_object_item (entry->dlna_profile),
entry->title,
protocol, entry->size,
entry->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, entry->id, entry->parent
? entry->parent->id : -1, "false",
entry->mime_type->mime_class, entry->title,
protocol, entry->size,
entry->url, filter);
didl_add_footer (out);
free (protocol);
for (c = index; c < MIN (index + count, entry->child_count); c++)
result_count++;
}
else /* container : directory */
{
didl_add_header (out);
didl_add_container (out, entry->id, entry->parent
? entry->parent->id : -1, entry->child_count,
"true", "true", entry->title,
entry->mime_type->mime_class);
didl_add_footer (out);
result_count = 1;
}
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, "1");
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, "1");
return result_count;
}
static int
cds_browse_directchildren (struct action_event_t *event,
struct buffer_t *out, int index,
int count, struct upnp_entry_t *entry, char *filter)
{
struct upnp_entry_t **childs;
int s, result_count = 0;
char tmp[32];
if (entry->child_count == -1) /* item : file */
return -1;
didl_add_header (out);
/* go to the child pointed out by index */
childs = entry->childs;
for (s = 0; s < index; s++)
if (*childs)
childs++;
/* UPnP CDS compliance : If starting index = 0 and requested count = 0
then all children must be returned */
if (index == 0 && count == 0)
count = entry->child_count;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
didl_add_container (out, (*childs)->id, (*childs)->parent ?
(*childs)->parent->id : -1,
(*childs)->child_count, "true", NULL,
(*childs)->title,
(*childs)->mime_type->mime_class);
else /* item */
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
}
result_count++;
}
}
didl_add_footer (out);
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
sprintf (tmp, "%d", entry->child_count);
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
return result_count;
}
static bool
cds_browse (struct action_event_t *event)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
int result_count = 0, index, count, id, sort_criteria;
char *flag = NULL;
char *filter = NULL;
struct buffer_t *out = NULL;
bool metadata;
if (!event)
return false;
/* Check for status */
if (!event->status)
return false;
/* check if metadatas have been well inited */
if (!ut->init)
return false;
/* Retrieve Browse arguments */
index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
flag = upnp_get_string (event->request, SERVICE_CDS_ARG_BROWSE_FLAG);
filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
if (!flag || !filter)
return false;
/* Check arguments validity */
if (!strcmp (flag, SERVICE_CDS_BROWSE_METADATA))
{
if (index)
{
free (flag);
return false;
}
metadata = true;
}
else if (!strcmp (flag, SERVICE_CDS_BROWSE_CHILDREN))
metadata = false;
else
{
free (flag);
return false;
}
free (flag);
entry = upnp_get_entry (ut, id);
if (!entry && (id < ut->starting_id))
entry = upnp_get_entry (ut, ut->starting_id);
if (!entry)
{
free (filter);
return false;
}
out = buffer_new ();
if (!out)
{
free (filter);
return false;
}
if (metadata)
result_count =
cds_browse_metadata (event, out, index, count, entry, filter);
else
result_count =
cds_browse_directchildren (event, out, index, count, entry, filter);
free (filter);
if (result_count < 0)
{
buffer_free (out);
return false;
}
buffer_free (out);
upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
return event->status;
}
static bool
matches_search (char *search_criteria, struct upnp_entry_t *entry)
{
char keyword[256] = SEARCH_OBJECT_KEYWORD;
bool derived_from = false, protocol_contains = false, result = false;
char *quote_closed = NULL, *and_clause = NULL;
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
entry->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, entry->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol (entry->mime_type);
if (!strncmp (search_criteria, SEARCH_CLASS_MATCH_KEYWORD,
strlen (SEARCH_CLASS_MATCH_KEYWORD)))
{
strncpy (keyword, search_criteria
+ strlen (SEARCH_CLASS_MATCH_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
else if (!strncmp (search_criteria, SEARCH_CLASS_DERIVED_KEYWORD,
strlen (SEARCH_CLASS_DERIVED_KEYWORD)))
{
derived_from = true;
strncpy (keyword, search_criteria
+ strlen (SEARCH_CLASS_DERIVED_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
else if (!strncmp (search_criteria, SEARCH_PROTOCOL_CONTAINS_KEYWORD,
strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD)))
{
protocol_contains = true;
strncpy (keyword, search_criteria
+ strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
if (derived_from && entry->mime_type
&& !strncmp (entry->mime_type->mime_class, keyword, strlen (keyword)))
result = true;
else if (protocol_contains && strstr (protocol, keyword))
result = true;
else if (entry->mime_type &&
!strcmp (entry->mime_type->mime_class, keyword))
result = true;
free (protocol);
and_clause = strstr (search_criteria, SEARCH_AND);
if (and_clause)
return (result &&
matches_search (and_clause + strlen (SEARCH_AND) -1, entry));
return true;
}
static int
cds_search_directchildren_recursive (struct buffer_t *out, int count,
struct upnp_entry_t *entry, char *filter,
char *search_criteria)
{
struct upnp_entry_t **childs;
int result_count = 0;
if (entry->child_count == -1) /* item : file */
return -1;
/* go to the first child */
childs = entry->childs;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
{
int new_count;
new_count = cds_search_directchildren_recursive
(out, (count == 0) ? 0 : (count - result_count),
(*childs), filter, search_criteria);
result_count += new_count;
}
else /* item */
{
if (matches_search (search_criteria, *childs))
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile):
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
result_count++;
}
}
}
}
return result_count;
}
static int
cds_search_directchildren (struct action_event_t *event,
struct buffer_t *out, int index,
int count, struct upnp_entry_t *entry,
char *filter, char *search_criteria)
{
struct upnp_entry_t **childs;
int s, result_count = 0;
char tmp[32];
index = 0;
if (entry->child_count == -1) /* item : file */
return -1;
didl_add_header (out);
/* go to the child pointed out by index */
childs = entry->childs;
for (s = 0; s < index; s++)
if (*childs)
childs++;
/* UPnP CDS compliance : If starting index = 0 and requested count = 0
then all children must be returned */
if (index == 0 && count == 0)
count = entry->child_count;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
{
int new_count;
new_count = cds_search_directchildren_recursive
(out, (count == 0) ? 0 : (count - result_count),
(*childs), filter, search_criteria);
result_count += new_count;
}
else /* item */
{
if (matches_search (search_criteria, *childs))
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile):
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
result_count++;
}
}
}
}
didl_add_footer (out);
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
return result_count;
}
static bool
cds_search (struct action_event_t *event)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
int result_count = 0, index, count, id, sort_criteria;
char *search_criteria = NULL;
char *filter = NULL;
struct buffer_t *out = NULL;
if (!event)
return false;
/* Check for status */
if (!event->status)
return false;
/* check if metadatas have been well inited */
if (!ut->init)
return false;
/* Retrieve Browse arguments */
index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
search_criteria = upnp_get_string (event->request,
SERVICE_CDS_ARG_SEARCH_CRIT);
filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
if (!search_criteria || !filter)
return false;
entry = upnp_get_entry (ut, id);
if (!entry && (id < ut->starting_id))
entry = upnp_get_entry (ut, ut->starting_id);
if (!entry)
return false;
out = buffer_new ();
if (!out)
return false;
result_count =
cds_search_directchildren (event, out, index, count, entry,
filter, search_criteria);
if (result_count < 0)
{
buffer_free (out);
return false;
}
buffer_free (out);
upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
free (search_criteria);
free (filter);
return event->status;
}
/* List of UPnP ContentDirectory Service actions */
struct service_action_t cds_service_actions[] = {
{ SERVICE_CDS_ACTION_SEARCH_CAPS, cds_get_search_capabilities },
{ SERVICE_CDS_ACTION_SORT_CAPS, cds_get_sort_capabilities },
{ SERVICE_CDS_ACTION_UPDATE_ID, cds_get_system_update_id },
{ SERVICE_CDS_ACTION_BROWSE, cds_browse },
{ SERVICE_CDS_ACTION_SEARCH, cds_search },
{ NULL, NULL }
};

212
src/cds.h Normal file
View File

@ -0,0 +1,212 @@
/*
* cds.h : GeeXboX uShare Content Directory Service header.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef CDS_H_
#define CDS_H_
#define CDS_DESCRIPTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
" <specVersion>" \
" <major>1</major>" \
" <minor>0</minor>" \
" </specVersion>" \
" <actionList>" \
" <action>" \
" <name>Browse</name>" \
" <argumentList>" \
" <argument>" \
" <name>ObjectID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>BrowseFlag</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Filter</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>StartingIndex</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>RequestedCount</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>SortCriteria</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Result</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>NumberReturned</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>TotalMatches</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>UpdateID</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>DestroyObject</name>" \
" <argumentList>" \
" <argument>" \
" <name>ObjectID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>GetSystemUpdateID</name>" \
" <argumentList>" \
" <argument>" \
" <name>Id</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>SystemUpdateID</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>GetSearchCapabilities</name>" \
" <argumentList>" \
" <argument>" \
" <name>SearchCaps</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>SearchCapabilities</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>GetSortCapabilities</name>" \
" <argumentList>" \
" <argument>" \
" <name>SortCaps</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>SortCapabilities</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>UpdateObject</name>" \
" <argumentList>" \
" <argument>" \
" <name>ObjectID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>CurrentTagValue</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>NewTagValue</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_TagValueList</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" </actionList>" \
" <serviceStateTable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_BrowseFlag</name>" \
" <dataType>string</dataType>" \
" <allowedValueList>" \
" <allowedValue>BrowseMetadata</allowedValue>" \
" <allowedValue>BrowseDirectChildren</allowedValue>" \
" </allowedValueList>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"yes\">" \
" <name>SystemUpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Count</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_SortCriteria</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>SortCapabilities</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Index</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_ObjectID</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_UpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_TagValueList</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Result</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>SearchCapabilities</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Filter</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" </serviceStateTable>" \
"</scpd>"
#define CDS_DESCRIPTION_LEN strlen (CDS_DESCRIPTION)
#define CDS_LOCATION "/web/cds.xml"
#define CDS_SERVICE_ID "urn:upnp-org:serviceId:ContentDirectory"
#define CDS_SERVICE_TYPE "urn:schemas-upnp-org:service:ContentDirectory:1"
#endif /* CDS_H_ */

450
src/cfgparser.c Normal file
View File

@ -0,0 +1,450 @@
/*
* cfgparser.c : GeeXboX uShare config file parser.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdbool.h>
#include <limits.h>
#include "config.h"
#include "gettext.h"
#include "cfgparser.h"
#include "ushare.h"
#include "trace.h"
#include "osdep.h"
#define USHARE_DIR_DELIM ","
static bool
ignore_line (const char *line)
{
int i;
size_t len;
/* commented line */
if (line[0] == '#' )
return true;
len = strlen (line);
for (i = 0 ; i < (int) len ; i++ )
if (line[i] != ' ' && line[i] != '\t' && line[i] != '\n')
return false;
return true;
}
static char *
strdup_trim (const char *s)
{
size_t begin, end;
char *r = NULL;
if (!s)
return NULL;
end = strlen (s) - 1;
for (begin = 0 ; begin < end ; begin++)
if (s[begin] != ' ' && s[begin] != '\t' && s[begin] != '"')
break;
for (; begin < end ; end--)
if (s[end] != ' ' && s[end] != '\t' && s[end] != '"' && s[end] != '\n')
break;
r = strndup (s + begin, end - begin + 1);
return r;
}
static void
ushare_set_name (struct ushare_t *ut, const char *name)
{
if (!ut || !name)
return;
if (ut->name)
{
free (ut->name);
ut->name = NULL;
}
ut->name = strdup_trim (name);
}
static void
ushare_set_interface (struct ushare_t *ut, const char *iface)
{
if (!ut || !iface)
return;
if (ut->interface)
{
free (ut->interface);
ut->interface = NULL;
}
ut->interface = strdup_trim (iface);
}
static void
ushare_add_contentdir (struct ushare_t *ut, const char *dir)
{
if (!ut || !dir)
return;
ut->contentlist = content_add (ut->contentlist, dir);
}
static void
ushare_set_cfg_file (struct ushare_t *ut, const char *file)
{
if (!ut || !file)
return;
ut->cfg_file = strdup (file);
}
static void
ushare_set_dir (struct ushare_t *ut, const char *dirlist)
{
char *x = NULL, *token = NULL;
char *m_buffer = NULL, *buffer;
if (!ut || !dirlist)
return;
x = strdup_trim (dirlist);
if (x)
{
m_buffer = (char*) malloc (strlen (x) * sizeof (char));
if (m_buffer)
{
buffer = m_buffer;
token = strtok_r (x, USHARE_DIR_DELIM, &buffer);
while (token)
{
ushare_add_contentdir (ut, token);
token = strtok_r (NULL, USHARE_DIR_DELIM, &buffer);
}
free (m_buffer);
}
free (x);
}
}
static void
ushare_set_port (struct ushare_t *ut, const char *port)
{
if (!ut || !port)
return;
ut->port = atoi (port);
if (ut->port < 49152)
{
fprintf (stderr,
_("Warning: port doesn't fit IANA port assignements.\n"));
fprintf (stderr, _("Warning: Only Dynamic or Private Ports can be used "
"(from 49152 through 65535)\n"));
ut->port = 0;
}
}
static void
ushare_set_telnet_port (struct ushare_t *ut, const char *port)
{
if (!ut || !port)
return;
ut->telnet_port = atoi (port);
}
static void
ushare_use_web (struct ushare_t *ut, const char *val)
{
if (!ut || !val)
return;
ut->use_presentation = (!strcmp (val, "yes")) ? true : false;
}
static void
ushare_use_telnet (struct ushare_t *ut, const char *val)
{
if (!ut || !val)
return;
ut->use_telnet = (!strcmp (val, "yes")) ? true : false;
}
static void
ushare_use_xbox (struct ushare_t *ut, const char *val)
{
if (!ut || !val)
return;
ut->xbox360 = (!strcmp (val, "yes")) ? true : false;
}
static void
ushare_use_dlna (struct ushare_t *ut, const char *val)
{
if (!ut || !val)
return;
#ifdef HAVE_DLNA
ut->dlna_enabled = (!strcmp (val, "yes")) ? true : false;
#endif /* HAVE_DLNA */
}
static void
ushare_set_override_iconv_err (struct ushare_t *ut, const char *arg)
{
if (!ut)
return;
ut->override_iconv_err = false;
if (!strcasecmp (arg, "yes")
|| !strcasecmp (arg, "true")
|| !strcmp (arg, "1"))
ut->override_iconv_err = true;
}
static u_configline_t configline[] = {
{ USHARE_NAME, ushare_set_name },
{ USHARE_IFACE, ushare_set_interface },
{ USHARE_PORT, ushare_set_port },
{ USHARE_TELNET_PORT, ushare_set_telnet_port },
{ USHARE_DIR, ushare_set_dir },
{ USHARE_OVERRIDE_ICONV_ERR, ushare_set_override_iconv_err },
{ USHARE_ENABLE_WEB, ushare_use_web },
{ USHARE_ENABLE_TELNET, ushare_use_telnet },
{ USHARE_ENABLE_XBOX, ushare_use_xbox },
{ USHARE_ENABLE_DLNA, ushare_use_dlna },
{ NULL, NULL },
};
static void
parse_config_line (struct ushare_t *ut, const char *line,
u_configline_t *configline)
{
char *s = NULL;
s = strchr (line, '=');
if (s && s[1] != '\0')
{
int i;
for (i=0 ; configline[i].name ; i++)
{
if (!strncmp (line, configline[i].name, strlen (configline[i].name)))
{
configline[i].set_var (ut, s + 1);
break;
}
}
}
}
int
parse_config_file (struct ushare_t *ut)
{
char filename[PATH_MAX];
FILE *conffile;
char *line = NULL;
size_t size = 0;
ssize_t read;
if (!ut)
return -1;
if (!ut->cfg_file)
snprintf (filename, PATH_MAX, "%s/%s", SYSCONFDIR, USHARE_CONFIG_FILE);
else
snprintf (filename, PATH_MAX, "%s", ut->cfg_file);
conffile = fopen (filename, "r");
if (!conffile)
return -1;
while ((read = getline (&line, &size, conffile)) != -1)
{
if (ignore_line (line))
continue;
if (line[read-1] == '\n')
line[read-1] = '\0';
while (line[0] == ' ' || line[0] == '\t')
line++;
parse_config_line (ut, line, configline);
}
fclose (conffile);
if (line)
free (line);
return 0;
}
inline static void
display_usage (void)
{
display_headers ();
printf ("\n");
printf (_("Usage: ushare [-n name] [-i interface] [-p port] [-c directory] [[-c directory]...]\n"));
printf (_("Options:\n"));
printf (_(" -n, --name=NAME\tSet UPnP Friendly Name (default is '%s')\n"),
DEFAULT_USHARE_NAME);
printf (_(" -i, --interface=IFACE\tUse IFACE Network Interface (default is '%s')\n"),
DEFAULT_USHARE_IFACE);
printf (_(" -f, --cfg=FILE\t\tConfig file to be used\n"));
printf (_(" -p, --port=PORT\tForces the HTTP server to run on PORT\n"));
printf (_(" -q, --telnet-port=PORT\tForces the TELNET server to run on PORT\n"));
printf (_(" -c, --content=DIR\tShare the content of DIR directory\n"));
printf (_(" -w, --no-web\t\tDisable the control web page (enabled by default)\n"));
printf (_(" -t, --no-telnet\tDisable the TELNET control (enabled by default)\n"));
printf (_(" -o, --override-iconv-err\tIf iconv fails parsing name, still add to media contents (hoping the renderer can handle it)\n"));
printf (_(" -v, --verbose\t\tSet verbose display\n"));
printf (_(" -x, --xbox\t\tUse XboX 360 compliant profile\n"));
#ifdef HAVE_DLNA
printf (_(" -d, --dlna\t\tUse DLNA compliant profile (PlayStation3 needs this)\n"));
#endif /* HAVE_DLNA */
printf (_(" -D, --daemon\t\tRun as a daemon\n"));
printf (_(" -V, --version\t\tDisplay the version of uShare and exit\n"));
printf (_(" -h, --help\t\tDisplay this help\n"));
}
int
parse_command_line (struct ushare_t *ut, int argc, char **argv)
{
int c, index;
char short_options[] = "VhvDowtxdn:i:p:q:c:f:";
struct option long_options [] = {
{"version", no_argument, 0, 'V' },
{"help", no_argument, 0, 'h' },
{"verbose", no_argument, 0, 'v' },
{"daemon", no_argument, 0, 'D' },
{"override-iconv-err", no_argument, 0, 'o' },
{"name", required_argument, 0, 'n' },
{"interface", required_argument, 0, 'i' },
{"port", required_argument, 0, 'p' },
{"telnet-port", required_argument, 0, 'q' },
{"content", required_argument, 0, 'c' },
{"no-web", no_argument, 0, 'w' },
{"no-telnet", no_argument, 0, 't' },
{"xbox", no_argument, 0, 'x' },
#ifdef HAVE_DLNA
{"dlna", no_argument, 0, 'd' },
#endif /* HAVE_DLNA */
{"cfg", required_argument, 0, 'f' },
{0, 0, 0, 0 }
};
/* command line argument processing */
while (true)
{
c = getopt_long (argc, argv, short_options, long_options, &index);
if (c == EOF)
break;
switch (c)
{
case 0:
/* opt = long_options[index].name; */
break;
case '?':
case 'h':
display_usage ();
return -1;
case 'V':
display_headers ();
return -1;
case 'v':
ut->verbose = true;
break;
case 'D':
ut->daemon = true;
break;
case 'o':
ut->override_iconv_err = true;
break;
case 'n':
ushare_set_name (ut, optarg);
break;
case 'i':
ushare_set_interface (ut, optarg);
break;
case 'p':
ushare_set_port (ut, optarg);
break;
case 'q':
ushare_set_telnet_port (ut, optarg);
break;
case 'c':
ushare_add_contentdir (ut, optarg);
break;
case 'w':
ut->use_presentation = false;
break;
case 't':
ut->use_telnet = false;
break;
case 'x':
ut->xbox360 = true;
break;
#ifdef HAVE_DLNA
case 'd':
ut->dlna_enabled = true;
break;
#endif /* HAVE_DLNA */
case 'f':
ushare_set_cfg_file (ut, optarg);
break;
default:
break;
}
}
return 0;
}

56
src/cfgparser.h Normal file
View File

@ -0,0 +1,56 @@
/*
* cfgparser.c : GeeXboX uShare config file parser headers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _CONFIG_PARSER_H_
#define _CONFIG_PARSER_H_
#include "ushare.h"
#define USHARE_NAME "USHARE_NAME"
#define USHARE_IFACE "USHARE_IFACE"
#define USHARE_PORT "USHARE_PORT"
#define USHARE_TELNET_PORT "USHARE_TELNET_PORT"
#define USHARE_DIR "USHARE_DIR"
#define USHARE_OVERRIDE_ICONV_ERR "USHARE_OVERRIDE_ICONV_ERR"
#define USHARE_ENABLE_WEB "USHARE_ENABLE_WEB"
#define USHARE_ENABLE_TELNET "USHARE_ENABLE_TELNET"
#define USHARE_ENABLE_XBOX "USHARE_ENABLE_XBOX"
#define USHARE_ENABLE_DLNA "USHARE_ENABLE_DLNA"
#define USHARE_CONFIG_FILE "ushare.conf"
#define DEFAULT_USHARE_NAME "uShare"
#if (defined(BSD) || defined(__FreeBSD__))
#define DEFAULT_USHARE_IFACE "lnc0"
#else /* Linux */
#define DEFAULT_USHARE_IFACE "eth0"
#endif
int parse_config_file (struct ushare_t *ut)
__attribute__ ((nonnull));
int parse_command_line (struct ushare_t *ut, int argc, char **argv)
__attribute__ ((nonnull (1)));
typedef struct {
char *name;
void (*set_var) (struct ushare_t*, const char*);
} u_configline_t;
#endif /* _CONFIG_PARSER_H_ */

180
src/cms.c Normal file
View File

@ -0,0 +1,180 @@
/*
* cms.c : GeeXboX uShare Connection Management Service.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "ushare.h"
#include "services.h"
#include "mime.h"
/* Represent the CMS GetProtocolInfo action. */
#define SERVICE_CMS_ACTION_PROT_INFO "GetProtocolInfo"
/* Represent the CMS GetCurrentConnectionIDs action. */
#define SERVICE_CMS_ACTION_CON_ID "GetCurrentConnectionIDs"
/* Represent the CMS GetCurrentConnectionInfo action. */
#define SERVICE_CMS_ACTION_CON_INFO "GetCurrentConnectionInfo"
/* Represent the CMS SOURCE argument. */
#define SERVICE_CMS_ARG_SOURCE "Source"
/* Represent the CMS SINK argument. */
#define SERVICE_CMS_ARG_SINK "Sink"
/* Represent the CMS ConnectionIDs argument. */
#define SERVICE_CMS_ARG_CONNECTION_IDS "ConnectionIDs"
/* Represent the CMS ConnectionID argument. */
#define SERVICE_CMS_ARG_CONNECTION_ID "ConnectionID"
/* Represent the CMS RcsID argument. */
#define SERVICE_CMS_ARG_RCS_ID "RcsID"
/* Represent the CMS AVTransportID argument. */
#define SERVICE_CMS_ARG_TRANSPORT_ID "AVTransportID"
/* Represent the CMS ProtocolInfo argument. */
#define SERVICE_CMS_ARG_PROT_INFO "ProtocolInfo"
/* Represent the CMS PeerConnectionManager argument. */
#define SERVICE_CMS_ARG_PEER_CON_MANAGER "PeerConnectionManager"
/* Represent the CMS PeerConnectionID argument. */
#define SERVICE_CMS_ARG_PEER_CON_ID "PeerConnectionID"
/* Represent the CMS Direction argument. */
#define SERVICE_CMS_ARG_DIRECTION "Direction"
/* Represent the CMS Status argument. */
#define SERVICE_CMS_ARG_STATUS "Status"
/* Represent the CMS default connection ID value. */
#define SERVICE_CMS_DEFAULT_CON_ID "0"
/* Represent the CMS unknown connection ID value. */
#define SERVICE_CMS_UNKNOW_ID "-1"
/* Represent the CMS Output value. */
#define SERVICE_CMS_OUTPUT "Output"
/* Represent the CMS Success Status. */
#define SERVICE_CMS_STATUS_OK "OK"
static bool
cms_get_protocol_info (struct action_event_t *event)
{
extern struct mime_type_t MIME_Type_List[];
struct mime_type_t *list;
char *respText = NULL, *respPtr;
size_t respLen = 0, len;
if (!event)
return false;
// calculating length of response
list = MIME_Type_List;
while (list->extension)
{
char *protocol = mime_get_protocol (list);
respLen += strlen (protocol) + 1;
free (protocol);
list++;
}
respText = (char*) malloc (respLen * sizeof (char));
if (!respText)
return event->status;
list = MIME_Type_List;
respPtr = respText;
while (list->extension)
{
char *protocol = mime_get_protocol (list);
len = strlen (protocol);
strncpy (respPtr, protocol, len);
free (protocol);
respPtr += len;
list++;
if (list->extension)
strcpy (respPtr++, ",");
}
*respPtr = '\0';
upnp_add_response (event, SERVICE_CMS_ARG_SOURCE, respText);
upnp_add_response (event, SERVICE_CMS_ARG_SINK, "");
free (respText);
return event->status;
}
static bool
cms_get_current_connection_ids (struct action_event_t *event)
{
if (!event)
return false;
upnp_add_response (event, SERVICE_CMS_ARG_CONNECTION_IDS, "");
return event->status;
}
static bool
cms_get_current_connection_info (struct action_event_t *event)
{
extern struct mime_type_t MIME_Type_List[];
struct mime_type_t *list = MIME_Type_List;
if (!event)
return false;
upnp_add_response (event, SERVICE_CMS_ARG_CONNECTION_ID,
SERVICE_CMS_DEFAULT_CON_ID);
upnp_add_response (event, SERVICE_CMS_ARG_RCS_ID, SERVICE_CMS_UNKNOW_ID);
upnp_add_response (event, SERVICE_CMS_ARG_TRANSPORT_ID,
SERVICE_CMS_UNKNOW_ID);
while (list->extension)
{
char *protocol = mime_get_protocol (list);
upnp_add_response (event, SERVICE_CMS_ARG_PROT_INFO, protocol);
free (protocol);
list++;
}
upnp_add_response (event, SERVICE_CMS_ARG_PEER_CON_MANAGER, "");
upnp_add_response (event, SERVICE_CMS_ARG_PEER_CON_ID,
SERVICE_CMS_UNKNOW_ID);
upnp_add_response (event, SERVICE_CMS_ARG_DIRECTION, SERVICE_CMS_OUTPUT);
upnp_add_response (event, SERVICE_CMS_ARG_STATUS, SERVICE_CMS_STATUS_OK);
return event->status;
}
/* List of UPnP ConnectionManager Service actions */
struct service_action_t cms_service_actions[] = {
{ SERVICE_CMS_ACTION_PROT_INFO, cms_get_protocol_info },
{ SERVICE_CMS_ACTION_CON_ID, cms_get_current_connection_ids },
{ SERVICE_CMS_ACTION_CON_INFO, cms_get_current_connection_info },
{ NULL, NULL }
};

166
src/cms.h Normal file
View File

@ -0,0 +1,166 @@
/*
* cms.h : GeeXboX uShare Connection Management Service header.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef CMS_H_
#define CMS_H_
#define CMS_DESCRIPTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
" <specVersion>" \
" <major>1</major>" \
" <minor>0</minor>" \
" </specVersion>" \
" <actionList>" \
" <action>" \
" <name>GetCurrentConnectionInfo</name>" \
" <argumentList>" \
" <argument>" \
" <name>ConnectionID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>RcsID</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_RcsID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>AVTransportID</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_AVTransportID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>ProtocolInfo</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ProtocolInfo</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>PeerConnectionManager</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ConnectionManager</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>PeerConnectionID</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ConnectionID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Direction</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Direction</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Status</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_ConnectionStatus</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>GetProtocolInfo</name>" \
" <argumentList>" \
" <argument>" \
" <name>Source</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>SourceProtocolInfo</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Sink</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>SinkProtocolInfo</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>GetCurrentConnectionIDs</name>" \
" <argumentList>" \
" <argument>" \
" <name>ConnectionIDs</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>CurrentConnectionIDs</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" </actionList>" \
" <serviceStateTable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_ProtocolInfo</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_ConnectionStatus</name>" \
" <dataType>string</dataType>" \
" <allowedValueList>" \
" <allowedValue>OK</allowedValue>" \
" <allowedValue>ContentFormatMismatch</allowedValue>" \
" <allowedValue>InsufficientBandwidth</allowedValue>" \
" <allowedValue>UnreliableChannel</allowedValue>" \
" <allowedValue>Unknown</allowedValue>" \
" </allowedValueList>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_AVTransportID</name>" \
" <dataType>i4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_RcsID</name>" \
" <dataType>i4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_ConnectionID</name>" \
" <dataType>i4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_ConnectionManager</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"yes\">" \
" <name>SourceProtocolInfo</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"yes\">" \
" <name>SinkProtocolInfo</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Direction</name>" \
" <dataType>string</dataType>" \
" <allowedValueList>" \
" <allowedValue>Input</allowedValue>" \
" <allowedValue>Output</allowedValue>" \
" </allowedValueList>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"yes\">" \
" <name>CurrentConnectionIDs</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" </serviceStateTable>" \
"</scpd>"
#define CMS_DESCRIPTION_LEN strlen (CMS_DESCRIPTION)
#define CMS_LOCATION "/web/cms.xml"
#define CMS_SERVICE_ID "urn:upnp-org:serviceId:ConnectionManager"
#define CMS_SERVICE_TYPE "urn:schemas-upnp-org:service:ConnectionManager:1"
#endif /* CMS_H_ */

93
src/content.c Normal file
View File

@ -0,0 +1,93 @@
/*
* content.c : GeeXboX uShare content list
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "content.h"
content_list *
content_add(content_list *list, const char *item)
{
if (!list)
{
list = (content_list*) malloc (sizeof(content_list));
list->content = NULL;
list->count = 0;
}
if (item)
{
list->count++;
list->content = (char**) realloc (list->content, list->count * sizeof(char*));
if (!list->content)
{
perror ("error realloc");
exit (2);
}
list->content[list->count-1] = strdup (item);
}
return list;
}
/*
* Remove the n'th content (start from 0)
*/
content_list *
content_del(content_list *list, int n)
{
int i;
if (!list || n >= list->count)
return NULL;
if (n >= list->count)
return list;
if (list->content[n])
{
free (list->content[n]);
for (i = n ; i < list->count - 1 ; i++)
list->content[i] = list->content[i+1];
list->count--;
list->content[list->count] = NULL;
}
return list;
}
void
content_free(content_list *list)
{
int i;
if (!list)
return;
if (list->content)
{
for (i=0 ; i < list->count ; i++)
{
if (list->content[i])
free (list->content[i]);
}
free (list->content);
}
free (list);
}

36
src/content.h Normal file
View File

@ -0,0 +1,36 @@
/*
* content.h : GeeXboX uShare content list header
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _CONTENT_H_
#define _CONTENT_H_
typedef struct content_list {
char **content;
int count;
} content_list;
content_list *content_add(content_list *list, const char *item)
__attribute__ ((malloc));
content_list *content_del(content_list *list, int n)
__attribute__ ((nonnull));
void content_free(content_list *list)
__attribute__ ((nonnull));
#endif

897
src/ctrl_telnet.c Normal file
View File

@ -0,0 +1,897 @@
/* ctrltelnet.c - Telnet controler
* Copyright (C) 2005-2007 Sven Almgren <sven@tras.se>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#define STR(x) _STR(x)
#define _STR(x) __STR(x)
#define __STR(x) #x
#include "config.h"
#include "ctrl_telnet.h"
#include "minmax.h"
#include "trace.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h> /* For select */
#include <sys/time.h>
#include <unistd.h> /* For pipe */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdarg.h>
#if (defined(____DISABLE_MUTEX) || 0)
#define pthread_mutex_lock(x) printf(">>>> Locking " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
#define pthread_mutex_unlock(x) printf("<<<< Unlocking " __FILE__ ":" STR(__LINE__) " \t" #x "\n");
#endif
/**
* @brief Structure holding data between the staring rutine and the thread
*/
typedef struct telnet_thread_data_t
{
pthread_t thread;
/* Litening socket */
int listener;
/* Socket used to terminate loop:
0 is reading and 1 is sending, kill by sending to 1 */
int killer[2];
/* Our socket address */
struct sockaddr_in local_address;
/* Shared data buffer that can be used by others... */
char shared_buffer[CTRL_TELNET_SHARED_BUFFER_SIZE];
ctrl_telnet_client *clients;
} telnet_thread_data;
/**
* @brief Struct for registerd commands
*/
typedef struct telnet_function_list_t
{
/* Function name, or keyword, if you like */
char *name;
char *description;
ctrl_telnet_command_ptr function;
struct telnet_function_list_t *next;
} telnet_function_list;
/* Static yes used to set socketoptions */
static int yes = 1;
static telnet_thread_data ttd;
static telnet_function_list* functions = NULL;
static pthread_mutex_t functions_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t startstop_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t shared_lock = PTHREAD_MUTEX_INITIALIZER;
static int started = 0;
/* Threadfunction, core in telnet controler */
/**
* @brief Thread function
*
* @param data Not used, leave as NULL
*/
static void *ctrl_telnet_thread (void *data);
/**
* @brief Adds a new client to our list of new ones
*
* @param client to add
*/
static void ctrl_telnet_client_add (ctrl_telnet_client *client);
/**
* @brief Removes "client" from our list of clients
*/
static void ctrl_telnet_client_remove (ctrl_telnet_client *client);
/**
* @brief Updates an fd_set to contain the current set of clients
*
* @return max fd found in list
*/
static int ctrl_telnet_fix_fdset (fd_set* readable);
static void ctrl_telnet_tokenize (char *raw, int *argc, char ***argv);
static int ctrl_telnet_client_recv (ctrl_telnet_client *client);
static int ctrl_telnet_client_execute (ctrl_telnet_client *client);
static int ctrl_telnet_client_execute_line (ctrl_telnet_client *client,
char *line);
static int ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client,
char *line);
static void ctrl_telnet_register_internals();
/**
* @brief Starts a Telnet bound control interface
*
* @return 0 on success, -1 on error
*/
int
ctrl_telnet_start (int port)
{
/* Start by making us threadsafe... */
pthread_mutex_lock (&startstop_lock);
/* Create listener socket */
ttd.listener = socket (PF_INET, SOCK_STREAM, 0);
if (ttd.listener == -1)
{
perror ("socket");
pthread_mutex_unlock (&startstop_lock);
return -1;
}
/* Clears us from "address already in use" errors */
if (setsockopt (ttd.listener, SOL_SOCKET, SO_REUSEADDR,
&yes, sizeof (int)) == -1)
perror ("setsockopt");
ttd.local_address.sin_family = AF_INET;
ttd.local_address.sin_addr.s_addr = INADDR_ANY;
ttd.local_address.sin_port = htons (port);
memset (&ttd.local_address.sin_zero, '\0',
sizeof (ttd.local_address.sin_zero));
if (bind (ttd.listener, (struct sockaddr *) &ttd.local_address,
sizeof (ttd.local_address)) == -1)
{
perror ("bind");
pthread_mutex_unlock (&startstop_lock);
return -1;
}
if (listen (ttd.listener, CTRL_TELNET_BACKLOG) == -1)
{
perror ("listen");
pthread_mutex_unlock (&startstop_lock);
return -1;
}
print_log (ULOG_NORMAL, "Listening on telnet port %u\n", port);
/* Create killer pipes */
if (pipe (ttd.killer))
{
perror ("Failed to create killer pipe");
pthread_mutex_unlock (&startstop_lock);
return -1; /* FIXME. Kill all sockets... not critical,, but still */
}
if (pthread_create (&ttd.thread, NULL, ctrl_telnet_thread, NULL))
{
/* FIXME: Killall sockets... */
perror ("Failed to create thread");
pthread_mutex_unlock (&startstop_lock);
return -1;
}
started = 1;
ctrl_telnet_register_internals ();
pthread_mutex_unlock (&startstop_lock);
return 0;
}
/**
* @brief Stops all telnet bound control interfaces
*/
void
ctrl_telnet_stop (void)
{
pthread_mutex_lock (&startstop_lock);
if (!started)
{
pthread_mutex_unlock (&startstop_lock);
return;
}
/* yes is int, which is bigger then char, so this should be safe */
write (ttd.killer[1], &yes, sizeof (char));
pthread_mutex_unlock (&startstop_lock);
pthread_join (ttd.thread, NULL);
}
/**
* @brief Telnet thread function
*/
static void *
ctrl_telnet_thread (void *a __attribute__ ((unused)))
{
/* fd_set with readable clients */
fd_set fd_readable;
/* Pointer to a client object */
ctrl_telnet_client *client;
int fd_max;
while (1)
{
/* Get fds */
fd_max = ctrl_telnet_fix_fdset (&fd_readable);
if (select (fd_max + 1, &fd_readable, NULL, NULL, NULL) == -1)
{
perror ("select");
/* FIXME: Close sockets */
return NULL;
}
/* Check killer */
if (FD_ISSET (ttd.killer[0], &fd_readable))
{
/* FIXME: TODO: Shut down sockets... */
/* Close listener and killer */
close (ttd.listener);
close (ttd.killer[0]);
close (ttd.killer[1]);
/* Check which fds that had anyhting to say... */
client = ttd.clients;
/* Say goodby to clients */
while (client)
{
ctrl_telnet_client *current = client;
ctrl_telnet_client_send (current,
"\nServer is going down, Bye bye\n");
client = client->next;
ctrl_telnet_client_remove (current);
}
pthread_mutex_lock (&functions_lock);
while (functions)
{
telnet_function_list *head = functions;
functions = functions->next;
free (head->name);
if (head->description)
free (head->description);
free (head);
}
pthread_mutex_unlock (&functions_lock);
return NULL;
}
/* Check for new connection */
if (FD_ISSET (ttd.listener, &fd_readable))
{
socklen_t sl_addr;
/* Create client object */
client = malloc (sizeof (ctrl_telnet_client));
if (!client)
{
perror ("Failed to create new client");
return NULL;
}
memset (client, '\0', sizeof (ctrl_telnet_client));
sl_addr = sizeof (client->remote_address);
client->socket = accept (ttd.listener,
(struct sockaddr *) &client->remote_address,
&sl_addr);
if (client->socket == -1)
{
perror ("accept");
free (client);
}
else
{
ctrl_telnet_client_add (client);
ctrl_telnet_client_execute_line_safe (client, "banner");
ctrl_telnet_client_sendf (client, "For a list of registered commands type \"help\"\n");
ctrl_telnet_client_send (client, "\n> ");
}
}
/* Check which fds that had anyhting to say... */
client = ttd.clients;
/* Run through all clients and check if there's data avalible
with FD_ISSET(current->socket) */
while (client)
{
ctrl_telnet_client *current = client;
client = client->next;
if (FD_ISSET (current->socket, &fd_readable))
{
if (ctrl_telnet_client_recv (current) <= 0)
{
ctrl_telnet_client_remove (current);
continue;
}
if (current->ready)
{
ctrl_telnet_client_execute (current);
if (!current->exiting)
ctrl_telnet_client_send (current, "\n> ");
else
ctrl_telnet_client_remove (current);
}
}
}
}
}
/**
* @brief Adds a new client to our list of new ones
*
* @note This funtion is only called from a single thread,
* as such it won't need to be threadsafe
* @param client to add
*/
static void
ctrl_telnet_client_add (ctrl_telnet_client *client)
{
client->next = ttd.clients;
ttd.clients = client;
}
/**
* @brief Removes "client" from our list of clients
*
* @note This funtion is only called from a single thread,
* as such it won't need to be threadsafe
* @param client to remove
*/
static void
ctrl_telnet_client_remove (ctrl_telnet_client *client)
{
ctrl_telnet_client *tmp;
/* Start by dealing with our head */
if (client == ttd.clients)
ttd.clients = client->next;
else
{
for (tmp = ttd.clients; tmp->next; tmp = tmp->next)
{
if (tmp->next == client)
{
tmp->next = tmp->next->next;
break;
}
}
}
close (client->socket);
free (client);
}
/**
* @brief Clears readable fd_set and adds every client to it,
* returns max fd found
*
* @param readable fd_set to update
* @return Biggest fd
*/
static int
ctrl_telnet_fix_fdset (fd_set *readable)
{
int maxfd;
ctrl_telnet_client *client;
maxfd = MAX (ttd.killer[0], ttd.listener);
FD_ZERO (readable);
FD_SET (ttd.listener, readable);
FD_SET (ttd.killer[0], readable);
client = ttd.clients;
while (client)
{
if (client->socket > maxfd)
maxfd = client->socket;
FD_SET (client->socket, readable);
client = client->next;
}
return maxfd;
}
static int
ctrl_telnet_client_recv (ctrl_telnet_client *client)
{
int i;
int nbytes;
int buffer_free = CTRL_CLIENT_RECV_BUFFER_SIZE - client->buffer_recv_current - 1;
nbytes = recv (client->socket,
client->buffer_recv + client->buffer_recv_current,
buffer_free, 0);
if (nbytes <= 0)
{
close (client->socket);
return nbytes;
}
client->buffer_recv_current += nbytes;
client->buffer_recv[client->buffer_recv_current] = '\0';
for (i = 0; i < client->buffer_recv_current; i++)
if (client->buffer_recv[i] == '\n')
client->ready = 1;
return nbytes;
}
int
ctrl_telnet_client_send (const ctrl_telnet_client *client, const char *string)
{
const char* cc = string;
int len = strlen (cc);
int sent = 0;
int senttotal = 0;
while ((cc - string) < len)
{
/* Use nonblocking just as a precation...
and a failed write won't _really_ kill us */
sent = send (client->socket, string, len - (cc - string), MSG_DONTWAIT);
/* This will mark the socket as dead... just to be safe..
and its only a telnet interface... reconnect and do it again */
if (sent == -1)
return -1;
senttotal += sent;
cc += sent;
}
return senttotal;
}
int
ctrl_telnet_client_sendf (const ctrl_telnet_client *client,
const char *format, ...)
{
int retval;
va_list ap;
int len;
pthread_mutex_lock (&shared_lock);
va_start (ap, format);
len = vsnprintf (ttd.shared_buffer,
CTRL_TELNET_SHARED_BUFFER_SIZE, format, ap);
va_end (ap);
/* Check if the message fitted inside the buffer, if not,
either exit or adjust len to be buffersize, I choose exit for now */
if (len >= CTRL_TELNET_SHARED_BUFFER_SIZE)
{
pthread_mutex_unlock (&shared_lock);
/* FIXME: Return error or send what we've got? */
return -1; /* Buffer was to small */
}
/* TODO: Might be good to have the option to specify str length so
send doesn't have to recompute it... */
retval = ctrl_telnet_client_send (client, ttd.shared_buffer);
pthread_mutex_unlock (&shared_lock);
return retval;
}
int
ctrl_telnet_client_sendsf (const ctrl_telnet_client *client,
char *buffer, int buffersize,
const char *format, ...)
{
va_list ap;
int len;
va_start (ap, format);
len = vsnprintf (buffer, buffersize, format, ap);
va_end (ap);
/* Check if the message fitted inside the buffer, if not,
either exit or adjust len to be buffersize, I choose exit for now */
if (len >= buffersize)
return -1; /* Buffer was to small */
/* TODO: Might be good to have the option to specify str length
so send doesn't have to recompute it... */
return ctrl_telnet_client_send (client, buffer);
}
/* FIXME: Ulgy non optimised version */
static int
ctrl_telnet_client_execute (ctrl_telnet_client *client)
{
int i = 0;
/* Check buffer for complete lines and execute them,,, */
for (i = 0; i < client->buffer_recv_current; i++)
{
if (client->buffer_recv[i] == '\n' || client->buffer_recv[i] == '\r')
{
/* Replace newline with null (or \r) */
client->buffer_recv[i] = '\0';
/* Send line to execution */
ctrl_telnet_client_execute_line_safe (client, client->buffer_recv);
/* Check if next is either newline or CR, strip that too, if needed */
if ((i + 1 < CTRL_CLIENT_RECV_BUFFER_SIZE) &&
(client->buffer_recv[i+1]=='\n' || client->buffer_recv[i+1]=='\r'))
client->buffer_recv[++i] = '\0';
/* Remove processed line */
memmove (client->buffer_recv, client->buffer_recv + i,
client->buffer_recv_current - 1);
client->buffer_recv_current -= (i + 1);
i = -1;
}
}
return 0; /* No syntax error checking yet */
}
static int
ctrl_telnet_client_execute_line_safe (ctrl_telnet_client *client, char *line)
{
int retval;
pthread_mutex_lock (&functions_lock);
retval = ctrl_telnet_client_execute_line (client, line);
pthread_mutex_unlock (&functions_lock);
return retval;
}
static int
ctrl_telnet_client_execute_line (ctrl_telnet_client *client, char *line)
{
int argc = 0;
char **argv = NULL;
telnet_function_list *node;
char *line2 = strdup (line); /* To make it safer */
ctrl_telnet_tokenize (line2, &argc, &argv);
node = functions;
if (*argv[0] == '\0')
{
free (argv);
free (line2);
return 0;
}
while (node)
{
if (!strcmp (node->name, argv[0]))
{
node->function (client, argc, argv);
break;
}
node = node->next;
}
if (!node)
ctrl_telnet_client_sendf (client, "%s: Command not found\n", argv[0]);
free (argv);
free (line2);
return strlen (line);
}
void
ctrl_telnet_register (const char *funcname,
ctrl_telnet_command_ptr funcptr,
const char *description)
{
telnet_function_list *function;
function = malloc (sizeof (telnet_function_list));
function->name = strdup (funcname); /* Mayby use strndup...? */
function->description = description ? strdup (description) : NULL;
function->function = funcptr;
pthread_mutex_lock (&functions_lock);
function->next = functions;
functions = function;
pthread_mutex_unlock (&functions_lock);
}
/* Warning: This WILL edit the input string... use strdup or something
if needed, also remember to free() argv as the first array is dynamic */
/* If *argv != NULL it'll first be free()ed... or realloc,
make sure to clear *argv to null on initialization */
static void
ctrl_telnet_tokenize (char *raw, int *argc, char ***argv)
{
int i;
int has_backslash = 0;
int has_quote = 0;
char *pc = raw;
if (!raw || !argc || !argv)
{
perror ("NULL in " __FILE__ " at line " STR (__LINE__));
return;
}
/* (1/3) First run is just to count our arguments... */
*argc = (raw[0] == '\0') ? 0 : 1;
pc = raw;
while (*pc)
{
switch (*pc)
{
case '\\':
if (!has_backslash)
has_backslash = 2; /* FULHACK */
break;
case ' ':
if (!has_backslash && !has_quote)
(*argc)++;
break;
case '"':
if (!has_backslash)
has_quote = !has_quote;
break;
}
/* When we get a BS we set it to two, this makes it one,
next run it will still be 1, then one after that is zero... FULHACK */
if (has_backslash)
has_backslash--;
pc++;
}
/* Create argv */
*argv = malloc (sizeof (char **) * ((*argc) + 1));
/* (2/3) Parse throu one more time, this time filling argv (Pass 2 / 3) */
i = 0;
pc = raw;
has_backslash = 0;
has_quote = 0;
(*argv)[0] = raw;
while (*pc)
{
switch (*pc)
{
case '\\':
if (!has_backslash)
has_backslash = 2; /* FULHACK */
break;
case ' ':
if (!has_backslash && !has_quote)
{
*pc = '\0';
(*argv)[++i] = pc+1;
pc++;
continue;
}
break;
case '"':
if (!has_backslash)
has_quote = !has_quote;
break;
}
/* When we get a BS we set it to two, this makes it one,
next run it will still be 1, then one after that is zero... FULHACK */
if (has_backslash)
has_backslash--;
pc++;
}
/* Make last element (argc) point to null... */
(*argv)[++i] = NULL;
/* (3/3) Parse arguments to remove escapings and such */
for (i = 0; (*argv)[i]; i++)
{
/* Set up environment */
pc = (*argv)[i];
has_backslash = 0;
has_quote = 0;
/* Remove leading and ending quotes, if existing */
if (*pc == '"')
{
int len = strlen (pc);
if (len > 0 && pc[len - 1] == '"')
pc[len - 1] = '\0';
memmove (pc, pc + 1, len);
}
/* Remove any special characters */
while (*pc)
{
switch (*pc)
{
case '\\':
if (!has_backslash)
{
has_backslash = 2; /* FULHACK */
break;
}
/* Else: fall through */
case ' ':
case '"':
if (has_backslash)
{
pc--;
memmove (pc, pc + 1, strlen (pc)); /* FIXME: Not cheap */
}
break;
}
/* When we get a BS we set it to two, this makes it one,
next run it will still be 1, then one after that is zero... */
if (has_backslash)
has_backslash--;
pc++;
}
}
}
static void
help (ctrl_telnet_client *client, int argc, char **argv)
{
int hidden = 0;
ctrl_telnet_client_execute_line (client, "banner");
if (argc < 2)
{
ctrl_telnet_client_send (client, "\n");
ctrl_telnet_client_send (client, "Usage: help TOPIC\n");
ctrl_telnet_client_send (client, "Valid topics are\n");
ctrl_telnet_client_send
(client, " commands - For a list of registed commands\n");
ctrl_telnet_client_send
(client, " syntax - For a description of the interface syntax\n");
return;
}
else
{
if (!strcmp ("commands", argv[1]))
{
telnet_function_list *node;
node = functions;
ctrl_telnet_client_send
(client, "Registered command (command - description)\n");
ctrl_telnet_client_send
(client, "=======================================================\n");
while (node)
{
/* Make functions without descriptions invisible */
if (node->description)
ctrl_telnet_client_sendf (client, " %s - %s\n",
node->name, node->description);
else
hidden++;
node = node->next;
}
if (hidden)
ctrl_telnet_client_sendf
(client, "There's also %i hidden functions\n", hidden);
} /* commands */
else if (!strcmp ("syntax", argv[1]))
{
ctrl_telnet_client_send
(client, "Syntax is easy: command parameters\n");
ctrl_telnet_client_send
(client, " Each new word is a new argument, unless the space is precided\n");
ctrl_telnet_client_send
(client, " a backslash (\\), or if a set of words are surrounded by quotes\n");
ctrl_telnet_client_send
(client, " (\"). To get a litteral quote you can escape it as \\\".\n");
ctrl_telnet_client_send (client, "\n");
ctrl_telnet_client_send (client, "STUB\n");
}
else
ctrl_telnet_client_send (client, "Unknown topic\n");
}
}
static void
banner (ctrl_telnet_client *client,
int argc __attribute__ ((unused)),
char **argv __attribute__ ((unused)))
{
ctrl_telnet_client_sendf (client, "%s (%s) (Built %s)\n",
PACKAGE_NAME, VERSION, __DATE__);
}
static void
echo (ctrl_telnet_client *client, int argc, char **argv)
{
int i;
for (i = 1; i < argc; i++)
ctrl_telnet_client_sendf (client, "%s%s", (i > 1 ? " " : ""), argv[i]);
ctrl_telnet_client_send (client, "\n");
}
static void
echod (ctrl_telnet_client *client, int argc, char **argv)
{
int i;
ctrl_telnet_client_sendf (client, "Argc: %i\n", argc);
for (i = 0; i < argc; i++)
ctrl_telnet_client_sendf (client, "%i: '%s'\n", i, argv[i]);
}
static void
ctrl_telnet_exit (ctrl_telnet_client *client,
int argc __attribute__ ((unused)),
char **argv __attribute__ ((unused)))
{
client->exiting = 1;
ctrl_telnet_client_send (client, "Bye bye\n");
}
static void
ctrl_telnet_register_internals (void)
{
ctrl_telnet_register ("echo", echo, "Echos all arguments");
ctrl_telnet_register ("echod", echod, "Echos all arguments but with each argument on a new line... DEBUG");
ctrl_telnet_register ("help", help, "Display help");
ctrl_telnet_register ("banner", banner, NULL);
ctrl_telnet_register ("exit", ctrl_telnet_exit, "Exits this interface (Or CTRL+D then Enter)");
/* CTRL+D... But it has to be fallowd by a new line */
ctrl_telnet_register ("\4", ctrl_telnet_exit, NULL);
}

75
src/ctrl_telnet.h Normal file
View File

@ -0,0 +1,75 @@
/* ctrltelnet.h - Header for the Telnet controler
* Copyright (C) 2005-2007 Sven Almgren <sven@tras.se>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef _CTRL_TELNET_H_
#define _CTRL_TELNET_H_
#define CTRL_TELNET_PORT 1337
#define CTRL_TELNET_BACKLOG 10
#define CTRL_TELNET_SHARED_BUFFER_SIZE 256
#define CTRL_CLIENT_RECV_BUFFER_SIZE 256
#include <netinet/in.h>
/**
* @brief Structure doubling as both a connected client data holder
* and as a linked list
*/
typedef struct ctrl_telnet_client_t
{
/* Recv buffer used to read single lines from more then one packet ...
Not garanteed to be NULL terminated */
char buffer_recv[CTRL_CLIENT_RECV_BUFFER_SIZE];
int buffer_recv_current;
int socket;
int ready; /* True if this client has a complete line, ready to be parsed */
int exiting;
struct sockaddr_in remote_address;
struct ctrl_telnet_client_t* next;
} ctrl_telnet_client;
typedef void (* ctrl_telnet_command_ptr) (ctrl_telnet_client *, int, char **);
/**
* @brief Starts a Telnet bound control interface
*
* @return 0 on success, -1 on error
*/
int ctrl_telnet_start (int port);
/**
* @brief Stops all telnet bound control interfaces
*/
void ctrl_telnet_stop (void);
/* FIXME: You can register a function name multiple times,
but the last one added is the one getting called... not a problem a.t.m. */
void ctrl_telnet_register (const char *funcname,
ctrl_telnet_command_ptr funcptr,
const char* description);
int ctrl_telnet_client_send (const ctrl_telnet_client *, const char* string);
int ctrl_telnet_client_sendf (const ctrl_telnet_client *client,
const char* format, ...);
int ctrl_telnet_client_sendsf (const ctrl_telnet_client *client,
char* buffer, int buffersize,
const char* format, ...);
#endif /* _CTRL_TELNET_H_ */

81
src/gettext.h Normal file
View File

@ -0,0 +1,81 @@
/* Convenience header for conditional use of GNU <libintl.h>.
Copyright (C) 1995-1998, 2000-2002, 2004 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA. */
#ifndef _LIBGETTEXT_H
#define _LIBGETTEXT_H 1
#define _(string) gettext (string)
/* NLS can be disabled through the configure --disable-nls option. */
#ifdef CONFIG_NLS
/* Get declarations of GNU message catalog functions. */
# include <libintl.h>
#else
/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
chokes if dcgettext is defined as a macro. So include it now, to make
later inclusions of <locale.h> a NOP. We don't include <libintl.h>
as well because people using "gettext.h" will not include <libintl.h>,
and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
is OK. */
#if defined(__sun)
# include <locale.h>
#endif
/* Many header files from the libstdc++ coming with g++ 3.3 or newer include
<libintl.h>, which chokes if dcgettext is defined as a macro. So include
it now, to make later inclusions of <libintl.h> a NOP. */
#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
# include <cstdlib>
# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H
# include <libintl.h>
# endif
#endif
/* Disabled NLS.
The casts to 'const char *' serve the purpose of producing warnings
for invalid uses of the value returned from these functions.
On pre-ANSI systems without 'const', the config.h file is supposed to
contain "#define const". */
# define gettext(Msgid) ((const char *) (Msgid))
# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dngettext(Domainname, Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define textdomain(Domainname) ((const char *) (Domainname))
# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
#endif
/* A pseudo function call that serves as a marker for the automated
extraction of messages, but does not call gettext(). The run-time
translation is done at a different place in the code.
The argument, String, should be a literal string. Concatenated strings
and other string expressions won't work.
The macro's expansion is not parenthesized, so that it is suitable as
initializer for static 'char[]' or 'const char[]' variables. */
#define gettext_noop(String) String
#endif /* _LIBGETTEXT_H */

414
src/http.c Normal file
View File

@ -0,0 +1,414 @@
/*
* http.c : GeeXboX uShare Web Server handler.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "services.h"
#include "cds.h"
#include "cms.h"
#include "msr.h"
#include "metadata.h"
#include "http.h"
#include "minmax.h"
#include "trace.h"
#include "presentation.h"
#include "osdep.h"
#include "mime.h"
#define PROTOCOL_TYPE_PRE_SZ 11 /* for the str length of "http-get:*:" */
#define PROTOCOL_TYPE_SUFF_SZ 2 /* for the str length of ":*" */
struct web_file_t {
char *fullpath;
off_t pos;
enum {
FILE_LOCAL,
FILE_MEMORY
} type;
union {
struct {
int fd;
struct upnp_entry_t *entry;
} local;
struct {
char *contents;
off_t len;
} memory;
} detail;
};
static inline void
set_info_file (struct File_Info *info, const size_t length,
const char *content_type)
{
info->file_length = length;
info->last_modified = 0;
info->is_directory = 0;
info->is_readable = 1;
info->content_type = ixmlCloneDOMString (content_type);
}
static int
http_get_info (const char *filename, struct File_Info *info)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
struct stat st;
int upnp_id = 0;
char *content_type = NULL;
char *protocol = NULL;
if (!filename || !info)
return -1;
log_verbose ("http_get_info, filename : %s\n", filename);
if (!strcmp (filename, CDS_LOCATION))
{
set_info_file (info, CDS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
return 0;
}
if (!strcmp (filename, CMS_LOCATION))
{
set_info_file (info, CMS_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
return 0;
}
if (!strcmp (filename, MSR_LOCATION))
{
set_info_file (info, MSR_DESCRIPTION_LEN, SERVICE_CONTENT_TYPE);
return 0;
}
if (ut->use_presentation && !strcmp (filename, USHARE_PRESENTATION_PAGE))
{
if (build_presentation_page (ut) < 0)
return -1;
set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
return 0;
}
if (ut->use_presentation && !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI)))
{
if (process_cgi (ut, (char *) (filename + strlen (USHARE_CGI) + 1)) < 0)
return -1;
set_info_file (info, ut->presentation->len, PRESENTATION_PAGE_CONTENT_TYPE);
return 0;
}
upnp_id = atoi (strrchr (filename, '/') + 1);
entry = upnp_get_entry (ut, upnp_id);
if (!entry)
return -1;
if (!entry->fullpath)
return -1;
if (stat (entry->fullpath, &st) < 0)
return -1;
if (access (entry->fullpath, R_OK) < 0)
{
if (errno != EACCES)
return -1;
info->is_readable = 0;
}
else
info->is_readable = 1;
/* file exist and can be read */
info->file_length = st.st_size;
info->last_modified = st.st_mtime;
info->is_directory = S_ISDIR (st.st_mode);
protocol =
#ifdef HAVE_DLNA
entry->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, entry->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol (entry->mime_type);
content_type =
strndup ((protocol + PROTOCOL_TYPE_PRE_SZ),
strlen (protocol + PROTOCOL_TYPE_PRE_SZ)
- PROTOCOL_TYPE_SUFF_SZ);
free (protocol);
if (content_type)
{
info->content_type = ixmlCloneDOMString (content_type);
free (content_type);
}
else
info->content_type = ixmlCloneDOMString ("");
return 0;
}
static UpnpWebFileHandle
get_file_memory (const char *fullpath, const char *description,
const size_t length)
{
struct web_file_t *file;
file = malloc (sizeof (struct web_file_t));
file->fullpath = strdup (fullpath);
file->pos = 0;
file->type = FILE_MEMORY;
file->detail.memory.contents = strdup (description);
file->detail.memory.len = length;
return ((UpnpWebFileHandle) file);
}
static UpnpWebFileHandle
http_open (const char *filename, enum UpnpOpenFileMode mode)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
struct web_file_t *file;
int fd, upnp_id = 0;
if (!filename)
return NULL;
log_verbose ("http_open, filename : %s\n", filename);
if (mode != UPNP_READ)
return NULL;
if (!strcmp (filename, CDS_LOCATION))
return get_file_memory (CDS_LOCATION, CDS_DESCRIPTION, CDS_DESCRIPTION_LEN);
if (!strcmp (filename, CMS_LOCATION))
return get_file_memory (CMS_LOCATION, CMS_DESCRIPTION, CMS_DESCRIPTION_LEN);
if (!strcmp (filename, MSR_LOCATION))
return get_file_memory (MSR_LOCATION, MSR_DESCRIPTION, MSR_DESCRIPTION_LEN);
if (ut->use_presentation && ( !strcmp (filename, USHARE_PRESENTATION_PAGE)
|| !strncmp (filename, USHARE_CGI, strlen (USHARE_CGI))))
return get_file_memory (USHARE_PRESENTATION_PAGE, ut->presentation->buf,
ut->presentation->len);
upnp_id = atoi (strrchr (filename, '/') + 1);
entry = upnp_get_entry (ut, upnp_id);
if (!entry)
return NULL;
if (!entry->fullpath)
return NULL;
log_verbose ("Fullpath : %s\n", entry->fullpath);
fd = open (entry->fullpath, O_RDONLY | O_NONBLOCK | O_SYNC | O_NDELAY);
if (fd < 0)
return NULL;
file = malloc (sizeof (struct web_file_t));
file->fullpath = strdup (entry->fullpath);
file->pos = 0;
file->type = FILE_LOCAL;
file->detail.local.entry = entry;
file->detail.local.fd = fd;
return ((UpnpWebFileHandle) file);
}
static int
http_read (UpnpWebFileHandle fh, char *buf, size_t buflen)
{
struct web_file_t *file = (struct web_file_t *) fh;
ssize_t len = -1;
log_verbose ("http_read\n");
if (!file)
return -1;
switch (file->type)
{
case FILE_LOCAL:
log_verbose ("Read local file.\n");
len = read (file->detail.local.fd, buf, buflen);
break;
case FILE_MEMORY:
log_verbose ("Read file from memory.\n");
len = (size_t) MIN (buflen, file->detail.memory.len - file->pos);
memcpy (buf, file->detail.memory.contents + file->pos, (size_t) len);
break;
default:
log_verbose ("Unknown file type.\n");
break;
}
if (len >= 0)
file->pos += len;
log_verbose ("Read %zd bytes.\n", len);
return len;
}
static int
http_write (UpnpWebFileHandle fh __attribute__((unused)),
char *buf __attribute__((unused)),
size_t buflen __attribute__((unused)))
{
log_verbose ("http write\n");
return 0;
}
static int
http_seek (UpnpWebFileHandle fh, off_t offset, int origin)
{
struct web_file_t *file = (struct web_file_t *) fh;
off_t newpos = -1;
log_verbose ("http_seek\n");
if (!file)
return -1;
switch (origin)
{
case SEEK_SET:
log_verbose ("Attempting to seek to %lld (was at %lld) in %s\n",
offset, file->pos, file->fullpath);
newpos = offset;
break;
case SEEK_CUR:
log_verbose ("Attempting to seek by %lld from %lld in %s\n",
offset, file->pos, file->fullpath);
newpos = file->pos + offset;
break;
case SEEK_END:
log_verbose ("Attempting to seek by %lld from end (was at %lld) in %s\n",
offset, file->pos, file->fullpath);
if (file->type == FILE_LOCAL)
{
struct stat sb;
if (stat (file->fullpath, &sb) < 0)
{
log_verbose ("%s: cannot stat: %s\n",
file->fullpath, strerror (errno));
return -1;
}
newpos = sb.st_size + offset;
}
else if (file->type == FILE_MEMORY)
newpos = file->detail.memory.len + offset;
break;
}
switch (file->type)
{
case FILE_LOCAL:
/* Just make sure we cannot seek before start of file. */
if (newpos < 0)
{
log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
return -1;
}
/* Don't seek with origin as specified above, as file may have
changed in size since our last stat. */
if (lseek (file->detail.local.fd, newpos, SEEK_SET) == -1)
{
log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (errno));
return -1;
}
break;
case FILE_MEMORY:
if (newpos < 0 || newpos > file->detail.memory.len)
{
log_verbose ("%s: cannot seek: %s\n", file->fullpath, strerror (EINVAL));
return -1;
}
break;
}
file->pos = newpos;
return 0;
}
static int
http_close (UpnpWebFileHandle fh)
{
struct web_file_t *file = (struct web_file_t *) fh;
log_verbose ("http_close\n");
if (!file)
return -1;
switch (file->type)
{
case FILE_LOCAL:
close (file->detail.local.fd);
break;
case FILE_MEMORY:
/* no close operation */
if (file->detail.memory.contents)
free (file->detail.memory.contents);
break;
default:
log_verbose ("Unknown file type.\n");
break;
}
if (file->fullpath)
free (file->fullpath);
free (file);
return 0;
}
struct UpnpVirtualDirCallbacks virtual_dir_callbacks =
{
http_get_info,
http_open,
http_read,
http_write,
http_seek,
http_close
};

30
src/http.h Normal file
View File

@ -0,0 +1,30 @@
/*
* http.h : GeeXboX uShare Web Server handler header.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _HTTP_H_
#define _HTTP_H_
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
struct UpnpVirtualDirCallbacks virtual_dir_callbacks;
#endif /* _HTTP_H_ */

595
src/metadata.c Normal file
View File

@ -0,0 +1,595 @@
/*
* metadata.c : GeeXboX uShare CDS Metadata DB.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdbool.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "mime.h"
#include "metadata.h"
#include "util_iconv.h"
#include "content.h"
#include "gettext.h"
#include "trace.h"
#define TITLE_UNKNOWN "unknown"
#define MAX_URL_SIZE 32
struct upnp_entry_lookup_t {
int id;
struct upnp_entry_t *entry_ptr;
};
static char *
getExtension (const char *filename)
{
char *str = NULL;
str = strrchr (filename, '.');
if (str)
str++;
return str;
}
static struct mime_type_t *
getMimeType (const char *extension)
{
extern struct mime_type_t MIME_Type_List[];
struct mime_type_t *list;
if (!extension)
return NULL;
list = MIME_Type_List;
while (list->extension)
{
if (!strcasecmp (list->extension, extension))
return list;
list++;
}
return NULL;
}
static bool
is_valid_extension (const char *extension)
{
if (!extension)
return false;
if (getMimeType (extension))
return true;
return false;
}
static int
get_list_length (void *list)
{
void **l = list;
int n = 0;
while (*(l++))
n++;
return n;
}
static xml_convert_t xml_convert[] = {
{'"' , "&quot;"},
{'&' , "&amp;"},
{'\'', "&apos;"},
{'<' , "&lt;"},
{'>' , "&gt;"},
{'\n', "&#xA;"},
{'\r', "&#xD;"},
{'\t', "&#x9;"},
{0, NULL},
};
static char *
get_xmlconvert (int c)
{
int j;
for (j = 0; xml_convert[j].xml; j++)
{
if (c == xml_convert[j].charac)
return xml_convert[j].xml;
}
return NULL;
}
static char *
convert_xml (const char *title)
{
char *newtitle, *s, *t, *xml;
int nbconvert = 0;
/* calculate extra size needed */
for (t = (char*) title; *t; t++)
{
xml = get_xmlconvert (*t);
if (xml)
nbconvert += strlen (xml) - 1;
}
if (!nbconvert)
return NULL;
newtitle = s = (char*) malloc (strlen (title) + nbconvert + 1);
for (t = (char*) title; *t; t++)
{
xml = get_xmlconvert (*t);
if (xml)
{
strcpy (s, xml);
s += strlen (xml);
}
else
*s++ = *t;
}
*s = '\0';
return newtitle;
}
static struct mime_type_t Container_MIME_Type =
{ NULL, "object.container.storageFolder", NULL};
static struct upnp_entry_t *
upnp_entry_new (struct ushare_t *ut, const char *name, const char *fullpath,
struct upnp_entry_t *parent, off_t size, int dir)
{
struct upnp_entry_t *entry = NULL;
char *title = NULL, *x = NULL;
char url_tmp[MAX_URL_SIZE] = { '\0' };
char *title_or_name = NULL;
if (!name)
return NULL;
entry = (struct upnp_entry_t *) malloc (sizeof (struct upnp_entry_t));
#ifdef HAVE_DLNA
entry->dlna_profile = NULL;
entry->url = NULL;
if (ut->dlna_enabled && fullpath && !dir)
{
dlna_profile_t *p = dlna_guess_media_profile (ut->dlna, fullpath);
if (!p)
{
free (entry);
return NULL;
}
entry->dlna_profile = p;
}
#endif /* HAVE_DLNA */
if (ut->xbox360)
{
if (ut->root_entry)
entry->id = ut->starting_id + ut->nr_entries++;
else
entry->id = 0; /* Creating the root node so don't use the usual IDs */
}
else
entry->id = ut->starting_id + ut->nr_entries++;
entry->fullpath = fullpath ? strdup (fullpath) : NULL;
entry->parent = parent;
entry->child_count = dir ? 0 : -1;
entry->title = NULL;
entry->childs = (struct upnp_entry_t **)
malloc (sizeof (struct upnp_entry_t *));
*(entry->childs) = NULL;
if (!dir) /* item */
{
#ifdef HAVE_DLNA
if (ut->dlna_enabled)
entry->mime_type = NULL;
else
{
#endif /* HAVE_DLNA */
struct mime_type_t *mime = getMimeType (getExtension (name));
if (!mime)
{
--ut->nr_entries;
upnp_entry_free (ut, entry);
log_error ("Invalid Mime type for %s, entry ignored", name);
return NULL;
}
entry->mime_type = mime;
#ifdef HAVE_DLNA
}
#endif /* HAVE_DLNA */
if (snprintf (url_tmp, MAX_URL_SIZE, "%d.%s",
entry->id, getExtension (name)) >= MAX_URL_SIZE)
log_error ("URL string too long for id %d, truncated!!", entry->id);
/* Only malloc() what we really need */
entry->url = strdup (url_tmp);
}
else /* container */
{
entry->mime_type = &Container_MIME_Type;
entry->url = NULL;
}
/* Try Iconv'ing the name but if it fails the end device
may still be able to handle it */
title = iconv_convert (name);
if (title)
title_or_name = title;
else
{
if (ut->override_iconv_err)
{
title_or_name = strdup (name);
log_error ("Entry invalid name id=%d [%s]\n", entry->id, name);
}
else
{
upnp_entry_free (ut, entry);
log_error ("Freeing entry invalid name id=%d [%s]\n", entry->id, name);
return NULL;
}
}
if (!dir)
{
x = strrchr (title_or_name, '.');
if (x) /* avoid displaying file extension */
*x = '\0';
}
x = convert_xml (title_or_name);
if (x)
{
free (title_or_name);
title_or_name = x;
}
entry->title = title_or_name;
if (!strcmp (title_or_name, "")) /* DIDL dc:title can't be empty */
{
free (title_or_name);
entry->title = strdup (TITLE_UNKNOWN);
}
entry->size = size;
entry->fd = -1;
if (entry->id && entry->url)
log_verbose ("Entry->URL (%d): %s\n", entry->id, entry->url);
return entry;
}
/* Seperate recursive free() function in order to avoid freeing off
* the parents child list within the freeing of the first child, as
* the only entry which is not part of a childs list is the root entry
*/
static void
_upnp_entry_free (struct upnp_entry_t *entry)
{
struct upnp_entry_t **childs;
if (!entry)
return;
if (entry->fullpath)
free (entry->fullpath);
if (entry->title)
free (entry->title);
if (entry->url)
free (entry->url);
#ifdef HAVE_DLNA
if (entry->dlna_profile)
entry->dlna_profile = NULL;
#endif /* HAVE_DLNA */
for (childs = entry->childs; *childs; childs++)
_upnp_entry_free (*childs);
free (entry->childs);
}
void
upnp_entry_free (struct ushare_t *ut, struct upnp_entry_t *entry)
{
if (!ut || !entry)
return;
/* Free all entries (i.e. children) */
if (entry == ut->root_entry)
{
struct upnp_entry_t *entry_found = NULL;
struct upnp_entry_lookup_t *lk = NULL;
RBLIST *rblist;
int i = 0;
rblist = rbopenlist (ut->rb);
lk = (struct upnp_entry_lookup_t *) rbreadlist (rblist);
while (lk)
{
entry_found = lk->entry_ptr;
if (entry_found)
{
if (entry_found->fullpath)
free (entry_found->fullpath);
if (entry_found->title)
free (entry_found->title);
if (entry_found->url)
free (entry_found->url);
free (entry_found);
i++;
}
free (lk); /* delete the lookup */
lk = (struct upnp_entry_lookup_t *) rbreadlist (rblist);
}
rbcloselist (rblist);
rbdestroy (ut->rb);
ut->rb = NULL;
log_verbose ("Freed [%d] entries\n", i);
}
else
_upnp_entry_free (entry);
free (entry);
}
static void
upnp_entry_add_child (struct ushare_t *ut,
struct upnp_entry_t *entry, struct upnp_entry_t *child)
{
struct upnp_entry_lookup_t *entry_lookup_ptr = NULL;
struct upnp_entry_t **childs;
int n;
if (!entry || !child)
return;
for (childs = entry->childs; *childs; childs++)
if (*childs == child)
return;
n = get_list_length ((void *) entry->childs) + 1;
entry->childs = (struct upnp_entry_t **)
realloc (entry->childs, (n + 1) * sizeof (*(entry->childs)));
entry->childs[n] = NULL;
entry->childs[n - 1] = child;
entry->child_count++;
entry_lookup_ptr = (struct upnp_entry_lookup_t *)
malloc (sizeof (struct upnp_entry_lookup_t));
entry_lookup_ptr->id = child->id;
entry_lookup_ptr->entry_ptr = child;
if (rbsearch ((void *) entry_lookup_ptr, ut->rb) == NULL)
log_info (_("Failed to add the RB lookup tree\n"));
}
struct upnp_entry_t *
upnp_get_entry (struct ushare_t *ut, int id)
{
struct upnp_entry_lookup_t *res, entry_lookup;
log_verbose ("Looking for entry id %d\n", id);
if (id == 0) /* We do not store the root (id 0) as it is not a child */
return ut->root_entry;
entry_lookup.id = id;
res = (struct upnp_entry_lookup_t *)
rbfind ((void *) &entry_lookup, ut->rb);
if (res)
{
log_verbose ("Found at %p\n",
((struct upnp_entry_lookup_t *) res)->entry_ptr);
return ((struct upnp_entry_lookup_t *) res)->entry_ptr;
}
log_verbose ("Not Found\n");
return NULL;
}
static void
metadata_add_file (struct ushare_t *ut, struct upnp_entry_t *entry,
const char *file, const char *name, struct stat *st_ptr)
{
if (!entry || !file || !name)
return;
#ifdef HAVE_DLNA
if (ut->dlna_enabled || is_valid_extension (getExtension (file)))
#else
if (is_valid_extension (getExtension (file)))
#endif
{
struct upnp_entry_t *child = NULL;
child = upnp_entry_new (ut, name, file, entry, st_ptr->st_size, false);
if (child)
upnp_entry_add_child (ut, entry, child);
}
}
static void
metadata_add_container (struct ushare_t *ut,
struct upnp_entry_t *entry, const char *container)
{
struct dirent **namelist;
int n,i;
if (!entry || !container)
return;
n = scandir (container, &namelist, 0, alphasort);
if (n < 0)
{
perror ("scandir");
return;
}
for (i = 0; i < n; i++)
{
struct stat st;
char *fullpath = NULL;
if (namelist[i]->d_name[0] == '.')
{
free (namelist[i]);
continue;
}
fullpath = (char *)
malloc (strlen (container) + strlen (namelist[i]->d_name) + 2);
sprintf (fullpath, "%s/%s", container, namelist[i]->d_name);
log_verbose ("%s\n", fullpath);
if (stat (fullpath, &st) < 0)
{
free (namelist[i]);
free (fullpath);
continue;
}
if (S_ISDIR (st.st_mode))
{
struct upnp_entry_t *child = NULL;
child = upnp_entry_new (ut, namelist[i]->d_name,
fullpath, entry, 0, true);
if (child)
{
metadata_add_container (ut, child, fullpath);
upnp_entry_add_child (ut, entry, child);
}
}
else
metadata_add_file (ut, entry, fullpath, namelist[i]->d_name, &st);
free (namelist[i]);
free (fullpath);
}
free (namelist);
}
void
free_metadata_list (struct ushare_t *ut)
{
ut->init = 0;
if (ut->root_entry)
upnp_entry_free (ut, ut->root_entry);
ut->root_entry = NULL;
ut->nr_entries = 0;
if (ut->rb)
{
rbdestroy (ut->rb);
ut->rb = NULL;
}
ut->rb = rbinit (rb_compare, NULL);
if (!ut->rb)
log_error (_("Cannot create RB tree for lookups\n"));
}
void
build_metadata_list (struct ushare_t *ut)
{
int i;
log_info (_("Building Metadata List ...\n"));
/* build root entry */
if (!ut->root_entry)
ut->root_entry = upnp_entry_new (ut, "root", NULL, NULL, -1, true);
/* add files from content directory */
for (i=0 ; i < ut->contentlist->count ; i++)
{
struct upnp_entry_t *entry = NULL;
char *title = NULL;
int size = 0;
log_info (_("Looking for files in content directory : %s\n"),
ut->contentlist->content[i]);
size = strlen (ut->contentlist->content[i]);
if (ut->contentlist->content[i][size - 1] == '/')
ut->contentlist->content[i][size - 1] = '\0';
title = strrchr (ut->contentlist->content[i], '/');
if (title)
title++;
else
{
/* directly use content directory name if no '/' before basename */
title = ut->contentlist->content[i];
}
entry = upnp_entry_new (ut, title, ut->contentlist->content[i],
ut->root_entry, -1, true);
if (!entry)
continue;
upnp_entry_add_child (ut, ut->root_entry, entry);
metadata_add_container (ut, entry, ut->contentlist->content[i]);
}
log_info (_("Found %d files and subdirectories.\n"), ut->nr_entries);
ut->init = 1;
}
int
rb_compare (const void *pa, const void *pb,
const void *config __attribute__ ((unused)))
{
struct upnp_entry_lookup_t *a, *b;
a = (struct upnp_entry_lookup_t *) pa;
b = (struct upnp_entry_lookup_t *) pb;
if (a->id < b->id)
return -1;
if (a->id > b->id)
return 1;
return 0;
}

60
src/metadata.h Normal file
View File

@ -0,0 +1,60 @@
/*
* metadata.h : GeeXboX uShare CDS Metadata DB header.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _METADATA_H_
#define _METADATA_H_
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include <stdbool.h>
#include <sys/types.h>
#include "ushare.h"
#include "http.h"
#include "content.h"
struct upnp_entry_t {
int id;
char *fullpath;
#ifdef HAVE_DLNA
dlna_profile_t *dlna_profile;
#endif /* HAVE_DLNA */
struct upnp_entry_t *parent;
int child_count;
struct upnp_entry_t **childs;
struct mime_type_t *mime_type;
char *title;
char *url;
off_t size;
int fd;
};
typedef struct xml_convert_s {
char charac;
char *xml;
} xml_convert_t;
void free_metadata_list (struct ushare_t *ut);
void build_metadata_list (struct ushare_t *ut);
struct upnp_entry_t *upnp_get_entry (struct ushare_t *ut, int id);
void upnp_entry_free (struct ushare_t *ut, struct upnp_entry_t *entry);
int rb_compare (const void *pa, const void *pb, const void *config);
#endif /* _METADATA_H_ */

150
src/mime.c Normal file
View File

@ -0,0 +1,150 @@
/*
* mime.c : GeeXboX uShare media file MIME-type association.
* Originally developped for the GeeXboX project.
* Ref : http://freedesktop.org/wiki/Standards_2fshared_2dmime_2dinfo_2dspec
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <string.h>
#include "mime.h"
#include "ushare.h"
#define UPNP_VIDEO "object.item.videoItem"
#define UPNP_AUDIO "object.item.audioItem.musicTrack"
#define UPNP_PHOTO "object.item.imageItem.photo"
#define UPNP_PLAYLIST "object.item.playlistItem"
#define UPNP_TEXT "object.item.textItem"
const struct mime_type_t MIME_Type_List[] = {
/* Video files */
{ "asf", UPNP_VIDEO, "http-get:*:video/x-ms-asf:"},
{ "avc", UPNP_VIDEO, "http-get:*:video/avi:"},
{ "avi", UPNP_VIDEO, "http-get:*:video/avi:"},
{ "dv", UPNP_VIDEO, "http-get:*:video/x-dv:"},
{ "divx", UPNP_VIDEO, "http-get:*:video/avi:"},
{ "wmv", UPNP_VIDEO, "http-get:*:video/x-ms-wmv:"},
{ "mjpg", UPNP_VIDEO, "http-get:*:video/x-motion-jpeg:"},
{ "mjpeg", UPNP_VIDEO, "http-get:*:video/x-motion-jpeg:"},
{ "mpeg", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "mpg", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "mpe", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "mp2p", UPNP_VIDEO, "http-get:*:video/mp2p:"},
{ "vob", UPNP_VIDEO, "http-get:*:video/mp2p:"},
{ "mp2t", UPNP_VIDEO, "http-get:*:video/mp2t:"},
{ "m1v", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "m2v", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
{ "mpg2", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
{ "mpeg2", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
{ "m4v", UPNP_VIDEO, "http-get:*:video/mp4:"},
{ "m4p", UPNP_VIDEO, "http-get:*:video/mp4:"},
{ "mp4ps", UPNP_VIDEO, "http-get:*:video/x-nerodigital-ps:"},
{ "ts", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
{ "ogm", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "mkv", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "rmvb", UPNP_VIDEO, "http-get:*:video/mpeg:"},
{ "mov", UPNP_VIDEO, "http-get:*:video/quicktime:"},
{ "hdmov", UPNP_VIDEO, "http-get:*:video/quicktime:"},
{ "qt", UPNP_VIDEO, "http-get:*:video/quicktime:"},
{ "bin", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
{ "iso", UPNP_VIDEO, "http-get:*:video/mpeg2:"},
/* Audio files */
{ "3gp", UPNP_AUDIO, "http-get:*:audio/3gpp:"},
{ "aac", UPNP_AUDIO, "http-get:*:audio/x-aac:"},
{ "ac3", UPNP_AUDIO, "http-get:*:audio/x-ac3:"},
{ "aif", UPNP_AUDIO, "http-get:*:audio/aiff:"},
{ "aiff", UPNP_AUDIO, "http-get:*:audio/aiff:"},
{ "at3p", UPNP_AUDIO, "http-get:*:audio/x-atrac3:"},
{ "au", UPNP_AUDIO, "http-get:*:audio/basic:"},
{ "snd", UPNP_AUDIO, "http-get:*:audio/basic:"},
{ "dts", UPNP_AUDIO, "http-get:*:audio/x-dts:"},
{ "rmi", UPNP_AUDIO, "http-get:*:audio/midi:"},
{ "mid", UPNP_AUDIO, "http-get:*:audio/midi:"},
{ "mp1", UPNP_AUDIO, "http-get:*:audio/mp1:"},
{ "mp2", UPNP_AUDIO, "http-get:*:audio/mp2:"},
{ "mp3", UPNP_AUDIO, "http-get:*:audio/mpeg:"},
{ "mp4", UPNP_AUDIO, "http-get:*:audio/mp4:"},
{ "m4a", UPNP_AUDIO, "http-get:*:audio/mp4:"},
{ "ogg", UPNP_AUDIO, "http-get:*:audio/x-ogg:"},
{ "wav", UPNP_AUDIO, "http-get:*:audio/wav:"},
{ "pcm", UPNP_AUDIO, "http-get:*:audio/l16:"},
{ "lpcm", UPNP_AUDIO, "http-get:*:audio/l16:"},
{ "l16", UPNP_AUDIO, "http-get:*:audio/l16:"},
{ "wma", UPNP_AUDIO, "http-get:*:audio/x-ms-wma:"},
{ "mka", UPNP_AUDIO, "http-get:*:audio/mpeg:"},
{ "ra", UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
{ "rm", UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
{ "ram", UPNP_AUDIO, "http-get:*:audio/x-pn-realaudio:"},
{ "flac", UPNP_AUDIO, "http-get:*:audio/x-flac:"},
/* Images files */
{ "bmp", UPNP_PHOTO, "http-get:*:image/bmp:"},
{ "ico", UPNP_PHOTO, "http-get:*:image/x-icon:"},
{ "gif", UPNP_PHOTO, "http-get:*:image/gif:"},
{ "jpeg", UPNP_PHOTO, "http-get:*:image/jpeg:"},
{ "jpg", UPNP_PHOTO, "http-get:*:image/jpeg:"},
{ "jpe", UPNP_PHOTO, "http-get:*:image/jpeg:"},
{ "pcd", UPNP_PHOTO, "http-get:*:image/x-ms-bmp:"},
{ "png", UPNP_PHOTO, "http-get:*:image/png:"},
{ "pnm", UPNP_PHOTO, "http-get:*:image/x-portable-anymap:"},
{ "ppm", UPNP_PHOTO, "http-get:*:image/x-portable-pixmap:"},
{ "qti", UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
{ "qtf", UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
{ "qtif", UPNP_PHOTO, "http-get:*:image/x-quicktime:"},
{ "tif", UPNP_PHOTO, "http-get:*:image/tiff:"},
{ "tiff", UPNP_PHOTO, "http-get:*:image/tiff:"},
/* Playlist files */
{ "pls", UPNP_PLAYLIST, "http-get:*:audio/x-scpls:"},
{ "m3u", UPNP_PLAYLIST, "http-get:*:audio/mpegurl:"},
{ "asx", UPNP_PLAYLIST, "http-get:*:video/x-ms-asf:"},
/* Subtitle Text files */
{ "srt", UPNP_TEXT, "http-get:*:text/srt:"}, /* SubRip */
{ "ssa", UPNP_TEXT, "http-get:*:text/ssa:"}, /* SubStation Alpha */
{ "stl", UPNP_TEXT, "http-get:*:text/srt:"}, /* Spruce */
{ "psb", UPNP_TEXT, "http-get:*:text/psb:"}, /* PowerDivX */
{ "pjs", UPNP_TEXT, "http-get:*:text/pjs:"}, /* Phoenix Japanim */
{ "sub", UPNP_TEXT, "http-get:*:text/sub:"}, /* MicroDVD */
{ "idx", UPNP_TEXT, "http-get:*:text/idx:"}, /* VOBsub */
{ "dks", UPNP_TEXT, "http-get:*:text/dks:"}, /* DKS */
{ "scr", UPNP_TEXT, "http-get:*:text/scr:"}, /* MACsub */
{ "tts", UPNP_TEXT, "http-get:*:text/tts:"}, /* TurboTitler */
{ "vsf", UPNP_TEXT, "http-get:*:text/vsf:"}, /* ViPlay */
{ "zeg", UPNP_TEXT, "http-get:*:text/zeg:"}, /* ZeroG */
{ "mpl", UPNP_TEXT, "http-get:*:text/mpl:"}, /* MPL */
/* Miscellaneous text files */
{ "bup", UPNP_TEXT, "http-get:*:text/bup:"}, /* DVD backup */
{ "ifo", UPNP_TEXT, "http-get:*:text/ifo:"}, /* DVD information */
{ NULL, NULL, NULL}
};
char *mime_get_protocol (struct mime_type_t *mime)
{
char protocol[512];
if (!mime)
return NULL;
sprintf (protocol, mime->mime_protocol);
strcat (protocol, "*");
return strdup (protocol);
}

32
src/mime.h Normal file
View File

@ -0,0 +1,32 @@
/*
* mime.h : GeeXboX uShare media file MIME-type association header.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _MIME_H_
#define _MIME_H_
struct mime_type_t {
char *extension;
char *mime_class;
char *mime_protocol;
};
char *mime_get_protocol (struct mime_type_t *mime);
#endif /* _MIME_H */

32
src/minmax.h Normal file
View File

@ -0,0 +1,32 @@
/* MIN, MAX macros.
* Copyright (C) 1995, 1998, 2001, 2003, 2005 Free Software Foundation, Inc.
*
* 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _MINMAX_H_
#define _MINMAX_H_
#include <limits.h>
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#endif /* _MINMAX_H_ */

92
src/msr.c Normal file
View File

@ -0,0 +1,92 @@
/*
* msr.c : GeeXboX uShare Microsoft Registrar Service.
* Originally developped for the GeeXboX project.
* Copyright (C) 2006 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "ushare.h"
#include "services.h"
/* Represent the MSR IsAuthorized action. */
#define SERVICE_MSR_ACTION_IS_AUTHORIZED "IsAuthorized"
/* Represent the MSR RegisterDevice action. */
#define SERVICE_MSR_ACTION_REGISTER_DEVICE "RegisterDevice"
/* Represent the MSR IsValidated action. */
#define SERVICE_MSR_ACTION_IS_VALIDATED "IsValidated"
/* Represent the MSR DeviceID argument. */
#define SERVICE_MSR_ARG_DEVICE_ID "DeviceID"
/* Represent the MSR Result argument. */
#define SERVICE_MSR_ARG_RESULT "Result"
/* Represent the MSR RegistrationReqMsg argument. */
#define SERVICE_MSR_ARG_REGISTRATION_REQUEST_MSG "RegistrationReqMsg"
/* Represent the MSR RegistrationRespMsg argument. */
#define SERVICE_MSR_ARG_REGISTRATION_RESPONSE_MSG "RegistrationRespMsg"
/* Represent the MSR Registered/Activated ID value. */
#define SERVICE_MSR_STATUS_OK "1"
static bool
msr_is_authorized (struct action_event_t *event)
{
if (!event)
return false;
/* send a fake authorization to these stupid MS players ;-) */
upnp_add_response (event, SERVICE_MSR_ARG_RESULT, SERVICE_MSR_STATUS_OK);
return event->status;
}
static bool
msr_register_device (struct action_event_t *event)
{
if (!event)
return false;
/* dummy action */
return event->status;
}
static bool
msr_is_validated (struct action_event_t *event)
{
if (!event)
return false;
/* send a fake validation to these stupid MS players ;-) */
upnp_add_response (event, SERVICE_MSR_ARG_RESULT, SERVICE_MSR_STATUS_OK);
return event->status;
}
/* List of UPnP Microsoft Registrar Service actions */
struct service_action_t msr_service_actions[] = {
{ SERVICE_MSR_ACTION_IS_AUTHORIZED, msr_is_authorized },
{ SERVICE_MSR_ACTION_REGISTER_DEVICE, msr_register_device },
{ SERVICE_MSR_ACTION_IS_VALIDATED, msr_is_validated },
{ NULL, NULL }
};

121
src/msr.h Normal file
View File

@ -0,0 +1,121 @@
/*
* msr.h : GeeXboX uShare Microsoft Registrar Service header.
* Originally developped for the GeeXboX project.
* Copyright (C) 2006 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MSR_H_
#define MSR_H_
#define MSR_DESCRIPTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">" \
"<specVersion>" \
" <major>1</major>" \
" <minor>0</minor>" \
"</specVersion>" \
"<actionList>" \
" <action>" \
" <name>IsAuthorized</name>" \
" <argumentList>" \
" <argument>" \
" <name>DeviceID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Result</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>RegisterDevice</name>" \
" <argumentList>" \
" <argument>" \
" <name>RegistrationReqMsg</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_RegistrationReqMsg</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>RegistrationRespMsg</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_RegistrationRespMsg</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
" <action>" \
" <name>IsValidated</name>" \
" <argumentList>" \
" <argument>" \
" <name>DeviceID</name>" \
" <direction>in</direction>" \
" <relatedStateVariable>A_ARG_TYPE_DeviceID</relatedStateVariable>" \
" </argument>" \
" <argument>" \
" <name>Result</name>" \
" <direction>out</direction>" \
" <relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable>" \
" </argument>" \
" </argumentList>" \
" </action>" \
"</actionList>" \
"<serviceStateTable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_DeviceID</name>" \
" <dataType>string</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_Result</name>" \
" <dataType>int</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_RegistrationReqMsg</name>" \
" <dataType>bin.base64</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>A_ARG_TYPE_RegistrationRespMsg</name>" \
" <dataType>bin.base64</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>AuthorizationGrantedUpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>AuthorizationDeniedUpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>ValidationSucceededUpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
" <stateVariable sendEvents=\"no\">" \
" <name>ValidationRevokedUpdateID</name>" \
" <dataType>ui4</dataType>" \
" </stateVariable>" \
"</serviceStateTable>" \
"</scpd>"
#define MSR_DESCRIPTION_LEN strlen (MSR_DESCRIPTION)
#define MSR_LOCATION "/web/msr.xml"
#define MSR_SERVICE_ID "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"
#define MSR_SERVICE_TYPE "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
#endif /* MSR_H_ */

91
src/osdep.c Normal file
View File

@ -0,0 +1,91 @@
/*
* osdep.c : GeeXboX uShare OS independant helpers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "osdep.h"
#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
char *
strndup (const char *s, size_t n)
{
size_t len;
char *sdup = NULL;
if (!s)
return NULL;
len = strlen (s);
len = n < len ? n : len;
sdup = (char *) malloc (len + 1);
if (sdup)
{
memcpy (sdup, s, len);
sdup[len] = '\0';
}
return sdup;
}
ssize_t
getline (char **lineptr, size_t *n, FILE *stream)
{
static char line[256];
char *ptr;
ssize_t len;
if (!lineptr || !n)
return -1;
if (ferror (stream))
return -1;
if (feof (stream))
return -1;
fgets (line, 256, stream);
ptr = strchr (line, '\n');
if (ptr)
*ptr = '\0';
len = strlen (line);
if ((len + 1) < 256)
{
ptr = realloc (*lineptr, 256);
if (!ptr)
return -1;
*lineptr = ptr;
*n = 256;
}
strcpy (*lineptr, line);
return len;
}
#endif /* (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__)) */

30
src/osdep.h Normal file
View File

@ -0,0 +1,30 @@
/*
* osdep.h : GeeXboX uShare OS independant helpers headers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _OS_DEP_H_
#define _OS_DEP_H_
#if (defined(BSD) || defined(__FreeBSD__))
#include <unistd.h>
char *strndup (const char *s, size_t n);
ssize_t getline (char **lineptr, size_t *n, FILE *stream);
#endif
#endif /* _OS_DEP_H_ */

215
src/presentation.c Normal file
View File

@ -0,0 +1,215 @@
/*
* presentation.c : GeeXboX uShare UPnP Presentation Page.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#if HAVE_LANGINFO_CODESET
# include <langinfo.h>
#endif
#include "config.h"
#include "metadata.h"
#include "content.h"
#include "buffer.h"
#include "presentation.h"
#include "gettext.h"
#include "util_iconv.h"
#define CGI_ACTION "action="
#define CGI_ACTION_ADD "add"
#define CGI_ACTION_DEL "del"
#define CGI_ACTION_REFRESH "refresh"
#define CGI_PATH "path"
#define CGI_SHARE "share"
int
process_cgi (struct ushare_t *ut, char *cgiargs)
{
char *action = NULL;
int refresh = 0;
if (!ut || !cgiargs)
return -1;
if (strncmp (cgiargs, CGI_ACTION, strlen (CGI_ACTION)))
return -1;
action = cgiargs + strlen (CGI_ACTION);
if (!strncmp (action, CGI_ACTION_ADD, strlen (CGI_ACTION_ADD)))
{
char *path = NULL;
path = action + strlen (CGI_ACTION_ADD) + 1;
if (path && !strncmp (path, CGI_PATH"=", strlen (CGI_PATH) + 1))
{
ut->contentlist = content_add (ut->contentlist,
path + strlen (CGI_PATH) + 1);
refresh = 1;
}
}
else if (!strncmp (action, CGI_ACTION_DEL, strlen (CGI_ACTION_DEL)))
{
char *shares,*share;
char *m_buffer = NULL, *buffer;
int num, shift=0;
shares = strdup (action + strlen (CGI_ACTION_DEL) + 1);
m_buffer = (char*) malloc (strlen (shares) * sizeof (char));
if (m_buffer)
{
buffer = m_buffer;
for (share = strtok_r (shares, "&", &buffer) ; share ;
share = strtok_r (NULL, "&", &buffer))
{
if (sscanf (share, CGI_SHARE"[%d]=on", &num) < 0)
continue;
ut->contentlist = content_del (ut->contentlist, num - shift++);
}
free (m_buffer);
}
refresh = 1;
free (shares);
}
else if (!strncmp (action, CGI_ACTION_REFRESH, strlen (CGI_ACTION_REFRESH)))
refresh = 1;
if (refresh && ut->contentlist)
{
free_metadata_list (ut);
build_metadata_list (ut);
}
if (ut->presentation)
buffer_free (ut->presentation);
ut->presentation = buffer_new ();
buffer_append (ut->presentation, "<html>");
buffer_append (ut->presentation, "<head>");
buffer_appendf (ut->presentation, "<title>%s</title>",
_("uShare Information Page"));
buffer_append (ut->presentation,
"<meta http-equiv=\"pragma\" content=\"no-cache\"/>");
buffer_append (ut->presentation,
"<meta http-equiv=\"expires\" content=\"1970-01-01\"/>");
buffer_append (ut->presentation,
"<meta http-equiv=\"refresh\" content=\"0; URL=/web/ushare.html\"/>");
buffer_append (ut->presentation, "</head>");
buffer_append (ut->presentation, "</html>");
return 0;
}
int
build_presentation_page (struct ushare_t *ut)
{
int i;
char *mycodeset = NULL;
if (!ut)
return -1;
if (ut->presentation)
buffer_free (ut->presentation);
ut->presentation = buffer_new ();
#if HAVE_LANGINFO_CODESET
mycodeset = nl_langinfo (CODESET);
#endif
if (!mycodeset)
mycodeset = UTF8;
buffer_append (ut->presentation, "<html>");
buffer_append (ut->presentation, "<head>");
buffer_appendf (ut->presentation, "<title>%s</title>",
_("uShare Information Page"));
buffer_appendf (ut->presentation,
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/>",
mycodeset);
buffer_append (ut->presentation,
"<meta http-equiv=\"pragma\" content=\"no-cache\"/>");
buffer_append (ut->presentation,
"<meta http-equiv=\"expires\" content=\"1970-01-01\"/>");
buffer_append (ut->presentation, "</head>");
buffer_append (ut->presentation, "<body>");
buffer_append (ut->presentation, "<h1 align=\"center\">");
buffer_appendf (ut->presentation, "<tt>%s</tt><br/>",
_("uShare UPnP A/V Media Server"));
buffer_append (ut->presentation, _("Information Page"));
buffer_append (ut->presentation, "</h1>");
buffer_append (ut->presentation, "<br/>");
buffer_append (ut->presentation, "<center>");
buffer_append (ut->presentation, "<tr width=\"500\">");
buffer_appendf (ut->presentation, "<b>%s :</b> %s<br/>",
_("Version"), VERSION);
buffer_append (ut->presentation, "</tr>");
buffer_appendf (ut->presentation, "<b>%s :</b> %s<br/>",
_("Device UDN"), ut->udn);
buffer_appendf (ut->presentation, "<b>%s :</b> %d<br/>",
_("Number of shared files and directories"), ut->nr_entries);
buffer_append (ut->presentation, "</center><br/>");
buffer_appendf (ut->presentation,
"<form method=\"get\" action=\"%s\">", USHARE_CGI);
buffer_appendf (ut->presentation,
"<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
CGI_ACTION_DEL);
for (i = 0 ; i < ut->contentlist->count ; i++)
{
buffer_appendf (ut->presentation, "<b>%s #%d :</b>", _("Share"), i + 1);
buffer_appendf (ut->presentation,
"<input type=\"checkbox\" name=\""CGI_SHARE"[%d]\"/>", i);
buffer_appendf (ut->presentation, "%s<br/>", ut->contentlist->content[i]);
}
buffer_appendf (ut->presentation,
"<input type=\"submit\" value=\"%s\"/>", _("unShare!"));
buffer_append (ut->presentation, "</form>");
buffer_append (ut->presentation, "<br/>");
buffer_appendf (ut->presentation,
"<form method=\"get\" action=\"%s\">", USHARE_CGI);
buffer_append (ut->presentation, _("Add a new share : "));
buffer_appendf (ut->presentation,
"<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
CGI_ACTION_ADD);
buffer_append (ut->presentation, "<input type=\"text\" name=\""CGI_PATH"\"/>");
buffer_appendf (ut->presentation,
"<input type=\"submit\" value=\"%s\"/>", _("Share!"));
buffer_append (ut->presentation, "</form>");
buffer_append (ut->presentation, "<br/>");
buffer_appendf (ut->presentation,
"<form method=\"get\" action=\"%s\">", USHARE_CGI);
buffer_appendf (ut->presentation,
"<input type=\"hidden\" name=\"action\" value=\"%s\"/>",
CGI_ACTION_REFRESH);
buffer_appendf (ut->presentation, "<input type=\"submit\" value=\"%s\"/>",
_("Refresh Shares ..."));
buffer_append (ut->presentation, "</form>");
buffer_append (ut->presentation, "</center>");
buffer_append (ut->presentation, "</body>");
buffer_append (ut->presentation, "</html>");
return 0;
}

31
src/presentation.h Normal file
View File

@ -0,0 +1,31 @@
/*
* presentation.h : GeeXboX uShare UPnP Presentation Page headers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _PRESENTATION_H_
#define _PRESENTATION_H_
#define USHARE_PRESENTATION_PAGE "/web/ushare.html"
#define PRESENTATION_PAGE_CONTENT_TYPE "text/html"
#define USHARE_CGI "/web/ushare.cgi"
int process_cgi (struct ushare_t *ut, char *cgiargs);
int build_presentation_page (struct ushare_t *ut);
#endif /* _PRESENTATION_H_ */

1140
src/redblack.c Normal file

File diff suppressed because it is too large Load Diff

188
src/redblack.h Normal file
View File

@ -0,0 +1,188 @@
/*
* RCS $Id: redblack.h,v 1.9 2003/10/24 01:31:21 damo Exp $
*/
/*
Redblack balanced tree algorithm
Copyright (C) Damian Ivereigh 2000
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See the file COPYING for details.
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 Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Header file for redblack.c, should be included by any code that
** uses redblack.c since it defines the functions
*/
/* Stop multiple includes */
#ifndef _REDBLACK_H
#ifndef RB_CUSTOMIZE
/*
* Without customization, the data member in the tree nodes is a void
* pointer, and you need to pass in a comparison function to be
* applied at runtime. With customization, you specify the data type
* as the macro RB_ENTRY(data_t) (has to be a macro because compilers
* gag on typdef void) and the name of the compare function as the
* value of the macro RB_CMP. Because the comparison function is
* compiled in, RB_CMP only needs to take two arguments. If your
* content type is not a pointer, define INLINE to get direct access.
*/
#define rbdata_t void
#define RB_CMP(s, t, e) (*rbinfo->rb_cmp)(s, t, e)
#undef RB_INLINE
#define RB_ENTRY(name) rb##name
#endif /* RB_CUSTOMIZE */
#ifndef RB_STATIC
#define RB_STATIC
#endif
/* Modes for rblookup */
#define RB_NONE -1 /* None of those below */
#define RB_LUEQUAL 0 /* Only exact match */
#define RB_LUGTEQ 1 /* Exact match or greater */
#define RB_LULTEQ 2 /* Exact match or less */
#define RB_LULESS 3 /* Less than key (not equal to) */
#define RB_LUGREAT 4 /* Greater than key (not equal to) */
#define RB_LUNEXT 5 /* Next key after current */
#define RB_LUPREV 6 /* Prev key before current */
#define RB_LUFIRST 7 /* First key in index */
#define RB_LULAST 8 /* Last key in index */
/* For rbwalk - pinched from search.h */
typedef enum
{
preorder,
postorder,
endorder,
leaf
}
VISIT;
struct RB_ENTRY(lists) {
const struct RB_ENTRY(node) *rootp;
const struct RB_ENTRY(node) *nextp;
};
#define RBLIST struct RB_ENTRY(lists)
struct RB_ENTRY(tree) {
#ifndef RB_CUSTOMIZE
/* comparison routine */
int (*rb_cmp)(const void *, const void *, const void *);
/* config data to be passed to rb_cmp */
const void *rb_config;
/* root of tree */
#endif /* RB_CUSTOMIZE */
struct RB_ENTRY(node) *rb_root;
};
#ifndef RB_CUSTOMIZE
RB_STATIC struct RB_ENTRY(tree) *rbinit(int (*)(const void *, const void *, const void *),
const void *);
#else
RB_STATIC struct RB_ENTRY(tree) *RB_ENTRY(init)(void);
#endif /* RB_CUSTOMIZE */
#ifndef no_delete
RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(delete)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
#endif
#ifndef no_find
RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(find)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
#endif
#ifndef no_lookup
RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(lookup)(int, const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
#endif
#ifndef no_search
RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(search)(const RB_ENTRY(data_t) *, struct RB_ENTRY(tree) *);
#endif
#ifndef no_destroy
RB_STATIC void RB_ENTRY(destroy)(struct RB_ENTRY(tree) *);
#endif
#ifndef no_walk
RB_STATIC void RB_ENTRY(walk)(const struct RB_ENTRY(tree) *,
void (*)(const RB_ENTRY(data_t) *, const VISIT, const int, void *),
void *);
#endif
#ifndef no_readlist
RB_STATIC RBLIST *RB_ENTRY(openlist)(const struct RB_ENTRY(tree) *);
RB_STATIC const RB_ENTRY(data_t) *RB_ENTRY(readlist)(RBLIST *);
RB_STATIC void RB_ENTRY(closelist)(RBLIST *);
#endif
/* Some useful macros */
#define rbmin(rbinfo) RB_ENTRY(lookup)(RB_LUFIRST, NULL, (rbinfo))
#define rbmax(rbinfo) RB_ENTRY(lookup)(RB_LULAST, NULL, (rbinfo))
#define _REDBLACK_H
#endif /* _REDBLACK_H */
/*
*
* $Log: redblack.h,v $
* Revision 1.9 2003/10/24 01:31:21 damo
* Patches from Eric Raymond: %prefix is implemented.  Various other small
* changes avoid stepping on global namespaces and improve the documentation.
*
* Revision 1.8 2003/10/23 04:18:47 damo
* Fixed up the rbgen stuff ready for the 1.3 release
*
* Revision 1.7 2002/08/26 03:11:40 damo
* Fixed up a bunch of compiler warnings when compiling example4
*
* Tidies up the Makefile.am & Specfile.
*
* Renamed redblack to rbgen
*
* Revision 1.6 2002/08/26 01:03:35 damo
* Patch from Eric Raymond to change the way the library is used:-
*
* Eric's idea is to convert libredblack into a piece of in-line code
* generated by another program. This should be faster, smaller and easier
* to use.
*
* This is the first check-in of his code before I start futzing with it!
*
* Revision 1.5 2002/01/30 07:54:53 damo
* Fixed up the libtool versioning stuff (finally)
* Fixed bug 500600 (not detecting a NULL return from malloc)
* Fixed bug 509485 (no longer needs search.h)
* Cleaned up debugging section
* Allow multiple inclusions of redblack.h
* Thanks to Matthias Andree for reporting (and fixing) these
*
* Revision 1.4 2000/06/06 14:43:43 damo
* Added all the rbwalk & rbopenlist stuff. Fixed up malloc instead of sbrk.
* Added two new examples
*
* Revision 1.3 2000/05/24 06:45:27 damo
* Converted everything over to using const
* Added a new example1.c file to demonstrate the worst case scenario
* Minor fixups of the spec file
*
* Revision 1.2 2000/05/24 06:17:10 damo
* Fixed up the License (now the LGPL)
*
* Revision 1.1 2000/05/24 04:15:53 damo
* Initial import of files. Versions are now all over the place. Oh well
*
*/

177
src/services.c Normal file
View File

@ -0,0 +1,177 @@
/*
* services.c : GeeXboX uShare UPnP services handler.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "ushare.h"
#include "services.h"
#include "cms.h"
#include "cds.h"
#include "msr.h"
#include "trace.h"
/* Represent the ObjectID argument. */
#define ARG_OBJECT_ID "ObjectID"
/* Represent the ContainerID argument. */
#define ARG_CONTAINER_ID "ContainerID"
extern struct service_action_t cds_service_actions[];
extern struct service_action_t cms_service_actions[];
extern struct service_action_t msr_service_actions[];
static struct service_t services[] = {
{
CDS_SERVICE_ID,
CDS_SERVICE_TYPE,
cds_service_actions
},
{
CMS_SERVICE_ID,
CMS_SERVICE_TYPE,
cms_service_actions
},
{
MSR_SERVICE_ID,
MSR_SERVICE_TYPE,
msr_service_actions
},
{ NULL, NULL, NULL }
};
bool
find_service_action (struct Upnp_Action_Request *request,
struct service_t **service,
struct service_action_t **action)
{
int c, d;
*service = NULL;
*action = NULL;
if (!request || !request->ActionName)
return false;
for (c = 0; services[c].id != NULL; c++)
if (!strcmp (services[c].id, request->ServiceID))
{
*service = &services[c];
for (d = 0; services[c].actions[d].name; d++)
{
if (!strcmp (services[c].actions[d].name, request->ActionName))
{
*action = &services[c].actions[d];
return true;
}
}
return false;
}
return false;
}
bool
upnp_add_response (struct action_event_t *event, char *key, const char *value)
{
char *val;
int res;
if (!event || !event->status || !key || !value)
return false;
val = strdup (value);
if (!val)
return false;
res = UpnpAddToActionResponse (&event->request->ActionResult,
event->request->ActionName,
event->service->type, key, val);
if (res != UPNP_E_SUCCESS)
{
free (val);
return false;
}
free (val);
return true;
}
char *
upnp_get_string (struct Upnp_Action_Request *request, const char *key)
{
IXML_Node *node = NULL;
if (!request || !request->ActionRequest || !key)
return NULL;
node = (IXML_Node *) request->ActionRequest;
if (!node)
{
log_verbose ("Invalid action request document\n");
return NULL;
}
node = ixmlNode_getFirstChild (node);
if (!node)
{
log_verbose ("Invalid action request document\n");
return NULL;
}
node = ixmlNode_getFirstChild (node);
for (; node; node = ixmlNode_getNextSibling (node))
if (!strcmp (ixmlNode_getNodeName (node), key))
{
node = ixmlNode_getFirstChild (node);
if (!node)
return strdup ("");
return strdup (ixmlNode_getNodeValue (node));
}
log_verbose ("Missing action request argument (%s)\n", key);
return NULL;
}
int
upnp_get_ui4 (struct Upnp_Action_Request *request, const char *key)
{
char *value;
int val;
if (!request || !key)
return 0;
value = upnp_get_string (request, key);
if (!value && !strcmp (key, ARG_OBJECT_ID))
value = upnp_get_string (request, ARG_CONTAINER_ID);
if (!value)
return 0;
val = atoi (value);
free (value);
return val;
}

53
src/services.h Normal file
View File

@ -0,0 +1,53 @@
/*
* services.h : GeeXboX uShare UPnP services handler header.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _SERVICES_H_
#define _SERVICES_H_
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include "ushare.h"
struct service_action_t {
char *name;
bool (*function) (struct action_event_t *);
};
struct service_t {
char *id;
char *type;
struct service_action_t *actions;
};
#define SERVICE_CONTENT_TYPE "text/xml"
bool find_service_action (struct Upnp_Action_Request *request,
struct service_t **service,
struct service_action_t **action);
bool upnp_add_response (struct action_event_t *event,
char *key, const char *value);
char * upnp_get_string (struct Upnp_Action_Request *request, const char *key);
int upnp_get_ui4 (struct Upnp_Action_Request *request, const char *key);
#endif /* _SERVICES_H_ */

64
src/trace.c Normal file
View File

@ -0,0 +1,64 @@
/*
* trace.c : GeeXboX uShare log facility.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdarg.h>
#include <syslog.h>
#include <stdbool.h>
#include "config.h"
#include "trace.h"
#include "ushare.h"
extern struct ushare_t *ut;
void
print_log (log_level level, const char *format, ...)
{
va_list va;
bool is_daemon = ut ? ut->daemon : false;
bool is_verbose = ut ? ut->verbose : false;
if (!format)
return;
if (!is_verbose && level >= ULOG_VERBOSE)
return;
va_start (va, format);
if (is_daemon)
{
int flags = LOG_DAEMON;
flags |= level == ULOG_ERROR ? LOG_ERR : LOG_NOTICE;
vsyslog (flags, format, va);
}
else
{
FILE *output = level == ULOG_ERROR ? stderr : stdout;
vfprintf (output, format, va);
}
va_end (va);
}
inline void
start_log (void)
{
openlog (PACKAGE_NAME, LOG_PID, LOG_DAEMON);
}

55
src/trace.h Normal file
View File

@ -0,0 +1,55 @@
/*
* trace.h : GeeXboX uShare log facility headers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _TRACE_H_
#define _TRACE_H_
typedef enum {
ULOG_NORMAL = 1,
ULOG_ERROR = 2,
ULOG_VERBOSE = 3,
} log_level;
void print_log (log_level level, const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
inline void start_log (void);
/* log_info
* Normal print, to replace printf
*/
#define log_info(s, str...) { \
print_log (ULOG_NORMAL, (s), ##str); \
}
/* log_error
* Error messages, output to stderr
*/
#define log_error(s, str...) { \
print_log (ULOG_ERROR, (s), ##str); \
}
/* log_verbose
* Output only in verbose mode
*/
#define log_verbose(s, str...) { \
print_log (ULOG_VERBOSE, (s), ##str); \
}
#endif /* _TRACE_H_ */

121
src/ushare.1 Normal file
View File

@ -0,0 +1,121 @@
.\" -*- nroff -*-
.\" ushare.1 - Manual page for uShare.
.\"
.\" Copyright (C) 2005-2007 Benjamin Zores
.\"
.\" 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
.\"
.TH USHARE 1 "July 05, 2007"
.SH NAME
uShare \(hy a lightweight UPnP A/V and DLNA Media Server
.SH SYNOPSIS
.B ushare
[\f-\-v\fR] [\f-\-n name\fR] [\f-\-i interface\fR] [\f-\-p port\fR] [\f-\-w\fR] [\f-\-c directory\fR] [[\f-\-c directory\fR]...]
.SH DESCRIPTION
\fBuShare\fP is a free UPnP / DLNA multimedia files Media Server.
It implements the server component that provides UPnP media devices with
information on available multimedia files. uShare uses the built-in http
server of libupnp to stream the files to clients.
.SH OPTIONS
This program follow the usual GNU command line syntax.
.TP
\fB\-\-name (\-n)\fR \fINAME\fR
Set UPnP Friendly Name (display name of the media server).
Default is 'uShare'.
.TP
\fB\-\-interface (\-i)\fR \fIINTERFACE\fR
Set Network interface to listen on.
Default is 'uShare'.
.TP
\fB\-\-cfg (\-f)\fR \fIFILE\fR
Config file to be used.
.TP
\fB\-\-port (\-p)\fR \fIPORT\fR
Set Network port to listen on.
Default is random above 49152.
.TP
\fB\-\-telnet_port (\-q)\fR \fIPORT\fR
Set Network port to listen on for telnet control connections.
Default is 1337.
.TP
\fB\-\-content (\-c)\fR \fIDIRECTORY\fR
Set directory to scan for multimedia files.
.TP
\fB\-\-no\-web (\-w)\fR
Disable the control web page (enabled by default).
.TP
\fB\-\-no\-telnet (\-t)\fR
Disable the telnet control (enabled by default).
.TP
\fB\-\-override-iconv-err (\-o)\fR
If iconv fails parsing name, still add to media contents
(hoping the renderer can handle it).
.TP
\fB\-\-verbose (\-v)\fR
Set verbose display.
.TP
\fB\-\-xbox (\-x)\fR
Use XboX 360 compliant profile.
.TP
\fB\-\-dlna (\-d)\fR
Use DLNA compliant profile (required for PlayStation3).
.TP
\fB\-\-daemon (\-D)\fR
Run as a daemon.
.TP
\fB\-\-version (\-V)\fR
Display uShare version number.
.TP
\fB\-\-help (\-h)\fR
Display help message.
.SH "REMOTE CONTROL"
You can also perform remote control of uShare UPnP Media Server through its
web interface. This let you define new content locations at runtime or
update the currently shared one in case the filesystem has changed.
To disable this feature, use the \-\-no\-web option (see bellow).
.TP
Just go to :
.TP
.B http://ip_address:port/web/ushare.html
.SH "FILE TYPES"
The following file formats (extensions) are supported :
.TP
.B Video
asf, avi, dv, divx, wmv, mjpg, mjpeg, mpeg, mpg, mpe, mp2p, vob, mp2t, m1v, m2v, m4v, m4p, mp4ps, ts, ogm, mkv, rmvb, mov, qt
.TP
.B Audio
aac, ac3, aif, aiff, at3p, au, snd, dts, rmi, mp1, mp2, mp3, mp4, mpa, ogg, wav, pcm, lpcm, l16, wma, mka, ra, rm, ram
.TP
.B Images
bmp, ico, gif, jpeg, jpg, jpe, pcd, png, pnm, ppm, qti, qtf, qtif, tif, tiff
.TP
.B Playlist
pls, m3u, asx
.TP
.B Subtitles
dks, idx, mpl, pjs, psb, scr, srt, ssa, stl, sub, tts, vsf, zeg
.TP
.B Miscellaneous files
bup, ifo
.SH "REPORTING BUGS"
Report bugs to <\fIushare@geexbox.org\fP>.
.SH AUTHOR
uShare was written by Benjamin Zores <ben@geexbox.org>.
.SH COPYRIGHT
Copyright \(co 2005-2007 Benjamin Zores
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

884
src/ushare.c Normal file
View File

@ -0,0 +1,884 @@
/*
* ushare.c : GeeXboX uShare UPnP Media Server.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>
#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if_dl.h>
#endif
#if (defined(__APPLE__))
#include <net/route.h>
#endif
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <fcntl.h>
#ifdef HAVE_IFADDRS_H
#include <ifaddrs.h>
#endif
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#if (defined(HAVE_SETLOCALE) && defined(CONFIG_NLS))
# include <locale.h>
#endif
#include "config.h"
#include "ushare.h"
#include "services.h"
#include "http.h"
#include "metadata.h"
#include "util_iconv.h"
#include "content.h"
#include "cfgparser.h"
#include "gettext.h"
#include "trace.h"
#include "buffer.h"
#include "ctrl_telnet.h"
struct ushare_t *ut = NULL;
static struct ushare_t * ushare_new (void)
__attribute__ ((malloc));
static struct ushare_t *
ushare_new (void)
{
struct ushare_t *ut = (struct ushare_t *) malloc (sizeof (struct ushare_t));
if (!ut)
return NULL;
ut->name = strdup (DEFAULT_USHARE_NAME);
ut->interface = strdup (DEFAULT_USHARE_IFACE);
ut->model_name = strdup (DEFAULT_USHARE_NAME);
ut->contentlist = NULL;
ut->rb = rbinit (rb_compare, NULL);
ut->root_entry = NULL;
ut->nr_entries = 0;
ut->starting_id = STARTING_ENTRY_ID_DEFAULT;
ut->init = 0;
ut->dev = 0;
ut->udn = NULL;
ut->ip = NULL;
ut->port = 0; /* Randomly attributed by libupnp */
ut->telnet_port = CTRL_TELNET_PORT;
ut->presentation = NULL;
ut->use_presentation = true;
ut->use_telnet = true;
#ifdef HAVE_DLNA
ut->dlna_enabled = false;
ut->dlna = NULL;
ut->dlna_flags = DLNA_ORG_FLAG_STREAMING_TRANSFER_MODE |
DLNA_ORG_FLAG_BACKGROUND_TRANSFERT_MODE |
DLNA_ORG_FLAG_CONNECTION_STALL |
DLNA_ORG_FLAG_DLNA_V15;
#endif /* HAVE_DLNA */
ut->xbox360 = false;
ut->verbose = false;
ut->daemon = false;
ut->override_iconv_err = false;
ut->cfg_file = NULL;
pthread_mutex_init (&ut->termination_mutex, NULL);
pthread_cond_init (&ut->termination_cond, NULL);
return ut;
}
static void
ushare_free (struct ushare_t *ut)
{
if (!ut)
return;
if (ut->name)
free (ut->name);
if (ut->interface)
free (ut->interface);
if (ut->model_name)
free (ut->model_name);
if (ut->contentlist)
content_free (ut->contentlist);
if (ut->rb)
rbdestroy (ut->rb);
if (ut->root_entry)
upnp_entry_free (ut, ut->root_entry);
if (ut->udn)
free (ut->udn);
if (ut->ip)
free (ut->ip);
if (ut->presentation)
buffer_free (ut->presentation);
#ifdef HAVE_DLNA
if (ut->dlna_enabled)
{
if (ut->dlna)
dlna_uninit (ut->dlna);
ut->dlna = NULL;
}
#endif /* HAVE_DLNA */
if (ut->cfg_file)
free (ut->cfg_file);
pthread_cond_destroy (&ut->termination_cond);
pthread_mutex_destroy (&ut->termination_mutex);
free (ut);
}
static void
ushare_signal_exit (void)
{
pthread_mutex_lock (&ut->termination_mutex);
pthread_cond_signal (&ut->termination_cond);
pthread_mutex_unlock (&ut->termination_mutex);
}
static void
handle_action_request (struct Upnp_Action_Request *request)
{
struct service_t *service;
struct service_action_t *action;
char val[256];
uint32_t ip;
if (!request || !ut)
return;
if (request->ErrCode != UPNP_E_SUCCESS)
return;
if (strcmp (request->DevUDN + 5, ut->udn))
return;
ip = request->CtrlPtIPAddr.s_addr;
ip = ntohl (ip);
sprintf (val, "%d.%d.%d.%d",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
if (ut->verbose)
{
DOMString str = ixmlPrintDocument (request->ActionRequest);
log_verbose ("***************************************************\n");
log_verbose ("** New Action Request **\n");
log_verbose ("***************************************************\n");
log_verbose ("ServiceID: %s\n", request->ServiceID);
log_verbose ("ActionName: %s\n", request->ActionName);
log_verbose ("CtrlPtIP: %s\n", val);
log_verbose ("Action Request:\n%s\n", str);
ixmlFreeDOMString (str);
}
if (find_service_action (request, &service, &action))
{
struct action_event_t event;
event.request = request;
event.status = true;
event.service = service;
if (action->function (&event) && event.status)
request->ErrCode = UPNP_E_SUCCESS;
if (ut->verbose)
{
DOMString str = ixmlPrintDocument (request->ActionResult);
log_verbose ("Action Result:\n%s", str);
log_verbose ("***************************************************\n");
log_verbose ("\n");
ixmlFreeDOMString (str);
}
return;
}
if (service) /* Invalid Action name */
strcpy (request->ErrStr, "Unknown Service Action");
else /* Invalid Service name */
strcpy (request->ErrStr, "Unknown Service ID");
request->ActionResult = NULL;
request->ErrCode = UPNP_SOAP_E_INVALID_ACTION;
}
static int
device_callback_event_handler (Upnp_EventType type, void *event,
void *cookie __attribute__((unused)))
{
switch (type)
{
case UPNP_CONTROL_ACTION_REQUEST:
handle_action_request ((struct Upnp_Action_Request *) event);
break;
case UPNP_CONTROL_ACTION_COMPLETE:
case UPNP_EVENT_SUBSCRIPTION_REQUEST:
case UPNP_CONTROL_GET_VAR_REQUEST:
break;
default:
break;
}
return 0;
}
static int
finish_upnp (struct ushare_t *ut)
{
if (!ut)
return -1;
log_info (_("Stopping UPnP Service ...\n"));
UpnpUnRegisterRootDevice (ut->dev);
UpnpFinish ();
return UPNP_E_SUCCESS;
}
static int
init_upnp (struct ushare_t *ut)
{
char *description = NULL;
int res;
size_t len;
if (!ut || !ut->name || !ut->udn || !ut->ip)
return -1;
#ifdef HAVE_DLNA
if (ut->dlna_enabled)
{
len = 0;
description =
dlna_dms_description_get (ut->name,
"GeeXboX Team",
"http://ushare.geexbox.org/",
"uShare : DLNA Media Server",
ut->model_name,
"001",
"http://ushare.geexbox.org/",
"USHARE-01",
ut->udn,
"/web/ushare.html",
"/web/cms.xml",
"/web/cms_control",
"/web/cms_event",
"/web/cds.xml",
"/web/cds_control",
"/web/cds_event");
if (!description)
return -1;
}
else
{
#endif /* HAVE_DLNA */
len = strlen (UPNP_DESCRIPTION) + strlen (ut->name)
+ strlen (ut->model_name) + strlen (ut->udn) + 1;
description = (char *) malloc (len * sizeof (char));
memset (description, 0, len);
sprintf (description, UPNP_DESCRIPTION, ut->name, ut->model_name, ut->udn);
#ifdef HAVE_DLNA
}
#endif /* HAVE_DLNA */
log_info (_("Initializing UPnP subsystem ...\n"));
res = UpnpInit (ut->ip, ut->port);
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot initialize UPnP subsystem\n"));
return -1;
}
if (UpnpSetMaxContentLength (UPNP_MAX_CONTENT_LENGTH) != UPNP_E_SUCCESS)
log_info (_("Could not set Max content UPnP\n"));
if (ut->xbox360)
log_info (_("Starting in XboX 360 compliant profile ...\n"));
#ifdef HAVE_DLNA
if (ut->dlna_enabled)
{
log_info (_("Starting in DLNA compliant profile ...\n"));
ut->dlna = dlna_init ();
dlna_set_verbosity (ut->dlna, ut->verbose ? 1 : 0);
dlna_set_extension_check (ut->dlna, 1);
dlna_register_all_media_profiles (ut->dlna);
}
#endif /* HAVE_DLNA */
ut->port = UpnpGetServerPort();
log_info (_("UPnP MediaServer listening on %s:%d\n"),
UpnpGetServerIpAddress (), ut->port);
UpnpEnableWebserver (TRUE);
res = UpnpSetVirtualDirCallbacks (&virtual_dir_callbacks);
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot set virtual directory callbacks\n"));
free (description);
return -1;
}
res = UpnpAddVirtualDir (VIRTUAL_DIR);
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot add virtual directory for web server\n"));
free (description);
return -1;
}
res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1,
device_callback_event_handler,
NULL, &(ut->dev));
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot register UPnP device\n"));
free (description);
return -1;
}
res = UpnpUnRegisterRootDevice (ut->dev);
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot unregister UPnP device\n"));
free (description);
return -1;
}
res = UpnpRegisterRootDevice2 (UPNPREG_BUF_DESC, description, 0, 1,
device_callback_event_handler,
NULL, &(ut->dev));
if (res != UPNP_E_SUCCESS)
{
log_error (_("Cannot register UPnP device\n"));
free (description);
return -1;
}
log_info (_("Sending UPnP advertisement for device ...\n"));
UpnpSendAdvertisement (ut->dev, 1800);
log_info (_("Listening for control point connections ...\n"));
if (description)
free (description);
return 0;
}
static bool
has_iface (char *interface)
{
#ifdef HAVE_IFADDRS_H
struct ifaddrs *itflist, *itf;
if (!interface)
return false;
if (getifaddrs (&itflist) < 0)
{
perror ("getifaddrs");
return false;
}
itf = itflist;
while (itf)
{
if ((itf->ifa_flags & IFF_UP)
&& !strncmp (itf->ifa_name, interface, IFNAMSIZ))
{
log_error (_("Interface %s is down.\n"), interface);
log_error (_("Recheck uShare's configuration and try again !\n"));
freeifaddrs (itflist);
return true;
}
itf = itf->ifa_next;
}
freeifaddrs (itf);
#else
int sock, i, n;
struct ifconf ifc;
struct ifreq ifr;
char buff[8192];
if (!interface)
return false;
/* determine UDN according to MAC address */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
return false;
}
/* get list of available interfaces */
ifc.ifc_len = sizeof (buff);
ifc.ifc_buf = buff;
if (ioctl (sock, SIOCGIFCONF, &ifc) < 0)
{
perror ("ioctl");
close (sock);
return false;
}
n = ifc.ifc_len / sizeof (struct ifreq);
for (i = n - 1 ; i >= 0 ; i--)
{
ifr = ifc.ifc_req[i];
if (strncmp (ifr.ifr_name, interface, IFNAMSIZ))
continue;
if (ioctl (sock, SIOCGIFFLAGS, &ifr) < 0)
{
perror ("ioctl");
close (sock);
return false;
}
if (!(ifr.ifr_flags & IFF_UP))
{
/* interface is down */
log_error (_("Interface %s is down.\n"), interface);
log_error (_("Recheck uShare's configuration and try again !\n"));
close (sock);
return false;
}
/* found right interface */
close (sock);
return true;
}
close (sock);
#endif
log_error (_("Can't find interface %s.\n"),interface);
log_error (_("Recheck uShare's configuration and try again !\n"));
return false;
}
static char *
create_udn (char *interface)
{
int sock = -1;
char *buf;
unsigned char *ptr;
#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
int mib[6];
size_t len;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
#else /* Linux */
struct ifreq ifr;
#endif
if (!interface)
return NULL;
#if (defined(BSD) || defined(__FreeBSD__) || defined(__APPLE__))
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
mib[5] = if_nametoindex (interface);
if (mib[5] == 0)
{
perror ("if_nametoindex");
return NULL;
}
if (sysctl (mib, 6, NULL, &len, NULL, 0) < 0)
{
perror ("sysctl");
return NULL;
}
buf = malloc (len);
if (sysctl (mib, 6, buf, &len, NULL, 0) < 0)
{
perror ("sysctl");
return NULL;
}
ifm = (struct if_msghdr *) buf;
sdl = (struct sockaddr_dl*) (ifm + 1);
ptr = (unsigned char *) LLADDR (sdl);
#else /* Linux */
/* determine UDN according to MAC address */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
return NULL;
}
strcpy (ifr.ifr_name, interface);
strcpy (ifr.ifr_hwaddr.sa_data, "");
if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
{
perror ("ioctl");
return NULL;
}
buf = (char *) malloc (64 * sizeof (char));
memset (buf, 0, 64);
ptr = (unsigned char *) ifr.ifr_hwaddr.sa_data;
#endif /* (defined(BSD) || defined(__FreeBSD__)) */
snprintf (buf, 64, "%s-%02x%02x%02x%02x%02x%02x", DEFAULT_UUID,
(ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
(ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377));
if (sock)
close (sock);
return buf;
}
static char *
get_iface_address (char *interface)
{
int sock;
uint32_t ip;
struct ifreq ifr;
char *val;
if (!interface)
return NULL;
/* determine UDN according to MAC address */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
return NULL;
}
strcpy (ifr.ifr_name, interface);
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)
{
perror ("ioctl");
close (sock);
return NULL;
}
val = (char *) malloc (16 * sizeof (char));
ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
ip = ntohl (ip);
sprintf (val, "%d.%d.%d.%d",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
close (sock);
return val;
}
static int
restart_upnp (struct ushare_t *ut)
{
finish_upnp (ut);
if (ut->udn)
free (ut->udn);
ut->udn = create_udn (ut->interface);
if (!ut->udn)
return -1;
if (ut->ip)
free (ut->ip);
ut->ip = get_iface_address (ut->interface);
if (!ut->ip)
return -1;
return (init_upnp (ut));
}
static void
UPnPBreak (int s __attribute__ ((unused)))
{
ushare_signal_exit ();
}
static void
reload_config (int s __attribute__ ((unused)))
{
struct ushare_t *ut2;
bool reload = false;
log_info (_("Reloading configuration...\n"));
ut2 = ushare_new ();
if (!ut || !ut2)
return;
if (parse_config_file (ut2) < 0)
return;
if (ut->name && strcmp (ut->name, ut2->name))
{
free (ut->name);
ut->name = ut2->name;
ut2->name = NULL;
reload = true;
}
if (ut->interface && strcmp (ut->interface, ut2->interface))
{
if (!has_iface (ut2->interface))
{
ushare_free (ut2);
raise (SIGINT);
}
else
{
free (ut->interface);
ut->interface = ut2->interface;
ut2->interface = NULL;
reload = true;
}
}
if (ut->port != ut2->port)
{
ut->port = ut2->port;
reload = true;
}
if (reload)
{
if (restart_upnp (ut) < 0)
{
ushare_free (ut2);
raise (SIGINT);
}
}
if (ut->contentlist)
content_free (ut->contentlist);
ut->contentlist = ut2->contentlist;
ut2->contentlist = NULL;
ushare_free (ut2);
if (ut->contentlist)
{
free_metadata_list (ut);
build_metadata_list (ut);
}
else
{
log_error (_("Error: no content directory to be shared.\n"));
raise (SIGINT);
}
}
inline void
display_headers (void)
{
printf (_("%s (version %s), a lightweight UPnP A/V and DLNA Media Server.\n"),
PACKAGE_NAME, VERSION);
printf (_("Benjamin Zores (C) 2005-2007, for GeeXboX Team.\n"));
printf (_("See http://ushare.geexbox.org/ for updates.\n"));
}
inline static void
setup_i18n(void)
{
#ifdef CONFIG_NLS
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
#endif
#if (!defined(BSD) && !defined(__FreeBSD__))
bindtextdomain (PACKAGE, LOCALEDIR);
#endif
textdomain (PACKAGE);
#endif
}
#define SHUTDOWN_MSG _("Server is shutting down: other clients will be notified soon, Bye bye ...\n")
static void
ushare_kill (ctrl_telnet_client *client,
int argc __attribute__((unused)),
char **argv __attribute__((unused)))
{
if (ut->use_telnet)
{
ctrl_telnet_client_send (client, SHUTDOWN_MSG);
client->exiting = true;
}
ushare_signal_exit ();
}
int
main (int argc, char **argv)
{
ut = ushare_new ();
if (!ut)
return EXIT_FAILURE;
setup_i18n ();
setup_iconv ();
/* Parse args before cfg file, as we may override the default file */
if (parse_command_line (ut, argc, argv) < 0)
{
ushare_free (ut);
return EXIT_SUCCESS;
}
if (parse_config_file (ut) < 0)
{
/* fprintf here, because syslog not yet ready */
fprintf (stderr, _("Warning: can't parse file \"%s\".\n"),
ut->cfg_file ? ut->cfg_file : SYSCONFDIR "/" USHARE_CONFIG_FILE);
}
if (ut->xbox360)
{
char *name;
name = malloc (strlen (XBOX_MODEL_NAME) + strlen (ut->model_name) + 4);
sprintf (name, "%s (%s)", XBOX_MODEL_NAME, ut->model_name);
free (ut->model_name);
ut->model_name = strdup (name);
free (name);
ut->starting_id = STARTING_ENTRY_ID_XBOX360;
}
if (ut->daemon)
{
/* starting syslog feature as soon as possible */
start_log ();
}
if (!ut->contentlist)
{
log_error (_("Error: no content directory to be shared.\n"));
ushare_free (ut);
return EXIT_FAILURE;
}
if (!has_iface (ut->interface))
{
ushare_free (ut);
return EXIT_FAILURE;
}
ut->udn = create_udn (ut->interface);
if (!ut->udn)
{
ushare_free (ut);
return EXIT_FAILURE;
}
ut->ip = get_iface_address (ut->interface);
if (!ut->ip)
{
ushare_free (ut);
return EXIT_FAILURE;
}
if (ut->daemon)
{
int err;
err = daemon (0, 0);
if (err == -1)
{
log_error (_("Error: failed to daemonize program : %s\n"),
strerror (err));
ushare_free (ut);
return EXIT_FAILURE;
}
}
else
{
display_headers ();
}
signal (SIGINT, UPnPBreak);
signal (SIGHUP, reload_config);
if (ut->use_telnet)
{
if (ctrl_telnet_start (ut->telnet_port) < 0)
{
ushare_free (ut);
return EXIT_FAILURE;
}
ctrl_telnet_register ("kill", ushare_kill,
_("Terminates the uShare server"));
}
if (init_upnp (ut) < 0)
{
finish_upnp (ut);
ushare_free (ut);
return EXIT_FAILURE;
}
build_metadata_list (ut);
/* Let main sleep until it's time to die... */
pthread_mutex_lock (&ut->termination_mutex);
pthread_cond_wait (&ut->termination_cond, &ut->termination_mutex);
pthread_mutex_unlock (&ut->termination_mutex);
if (ut->use_telnet)
ctrl_telnet_stop ();
finish_upnp (ut);
free_metadata_list (ut);
ushare_free (ut);
finish_iconv ();
/* it should never be executed */
return EXIT_SUCCESS;
}

132
src/ushare.h Normal file
View File

@ -0,0 +1,132 @@
/*
* ushare.h : GeeXboX uShare UPnP Media Server header.
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _USHARE_H_
#define _USHARE_H_
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include <stdbool.h>
#include <pthread.h>
#ifdef HAVE_DLNA
#include <dlna.h>
#endif /* HAVE_DLNA */
#include "content.h"
#include "buffer.h"
#include "redblack.h"
#define VIRTUAL_DIR "/web"
#define XBOX_MODEL_NAME "Windows Media Connect Compatible"
#define DEFAULT_UUID "898f9738-d930-4db4-a3cf"
#define UPNP_MAX_CONTENT_LENGTH 4096
#define STARTING_ENTRY_ID_DEFAULT 0
#define STARTING_ENTRY_ID_XBOX360 100000
#define UPNP_DESCRIPTION \
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" \
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">" \
" <specVersion>" \
" <major>1</major>" \
" <minor>0</minor>" \
" </specVersion>" \
" <device>" \
" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>" \
" <friendlyName>%s: 1</friendlyName>" \
" <manufacturer>GeeXboX Team</manufacturer>" \
" <manufacturerURL>http://ushare.geexbox.org/</manufacturerURL>" \
" <modelDescription>GeeXboX uShare : UPnP Media Server</modelDescription>" \
" <modelName>%s</modelName>" \
" <modelNumber>001</modelNumber>" \
" <modelURL>http://ushare.geexbox.org/</modelURL>" \
" <serialNumber>GEEXBOX-USHARE-01</serialNumber>" \
" <UDN>uuid:%s</UDN>" \
" <serviceList>" \
" <service>" \
" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>" \
" <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>" \
" <SCPDURL>/web/cms.xml</SCPDURL>" \
" <controlURL>/web/cms_control</controlURL>" \
" <eventSubURL>/web/cms_event</eventSubURL>" \
" </service>" \
" <service>" \
" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>" \
" <serviceId>urn:upnp-org:serviceId:ContentDirectory</serviceId>" \
" <SCPDURL>/web/cds.xml</SCPDURL>" \
" <controlURL>/web/cds_control</controlURL>" \
" <eventSubURL>/web/cds_event</eventSubURL>" \
" </service>" \
" <service>" \
" <serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType>\n" \
" <serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId>\n" \
" <SCPDURL>/web/msr.xml</SCPDURL>" \
" <controlURL>/web/msr_control</controlURL>" \
" <eventSubURL>/web/msr_event</eventSubURL>" \
" </service>\n" \
" </serviceList>" \
" <presentationURL>/web/ushare.html</presentationURL>" \
" </device>" \
"</root>"
struct ushare_t {
char *name;
char *interface;
char *model_name;
content_list *contentlist;
struct rbtree *rb;
struct upnp_entry_t *root_entry;
int nr_entries;
int starting_id;
int init;
UpnpDevice_Handle dev;
char *udn;
char *ip;
unsigned short port;
unsigned short telnet_port;
struct buffer_t *presentation;
bool use_presentation;
bool use_telnet;
#ifdef HAVE_DLNA
bool dlna_enabled;
dlna_t *dlna;
dlna_org_flags_t dlna_flags;
#endif /* HAVE_DLNA */
bool xbox360;
bool verbose;
bool daemon;
bool override_iconv_err;
char *cfg_file;
pthread_mutex_t termination_mutex;
pthread_cond_t termination_cond;
};
struct action_event_t {
struct Upnp_Action_Request *request;
bool status;
struct service_t *service;
};
inline void display_headers (void);
#endif /* _USHARE_H_ */

170
src/util_iconv.c Normal file
View File

@ -0,0 +1,170 @@
/*
* util_iconv.c : GeeXboX uShare iconv string encoding utlities.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "util_iconv.h"
#if HAVE_ICONV
#include <iconv.h>
static iconv_t cd = 0;
#endif
#if HAVE_LANGINFO_CODESET
#include <langinfo.h>
#endif
void
setup_iconv (void)
{
#if HAVE_ICONV && HAVE_LANGINFO_CODESET
char *mycodeset = NULL;
mycodeset = nl_langinfo (CODESET);
if (!mycodeset)
return;
/**
* Setup conversion descriptor if user's console is non-UTF-8. Otherwise
* we can just leave cd as NULL
*/
if (strcmp (mycodeset, UTF8))
{
cd = iconv_open (UTF8, mycodeset);
if (cd == (iconv_t) (-1))
{
perror ("iconv_open");
cd = 0;
}
}
#endif
}
void
finish_iconv (void)
{
#if HAVE_ICONV
if (!cd)
return;
if (iconv_close (cd) < 0)
perror ("iconv_close");
cd = 0;
#endif
}
/**
* iconv_convert : convert a string, using the current codeset
* return: a malloc'd string with the converted result
*/
char *
iconv_convert (const char *input)
{
#if HAVE_ICONV
size_t inputsize = strlen (input) + 1;
size_t dummy = 0;
size_t length = 0;
char *result;
char *inptr, *outptr;
size_t insize, outsize;
/* conversion not necessary. save our time. */
if (!cd)
return strdup (input);
/* Determine the length we need. */
iconv (cd, NULL, NULL, NULL, &dummy);
{
static char tmpbuf[BUFSIZ];
inptr = (char*) input;
insize = inputsize;
while (insize > 0)
{
outptr = tmpbuf;
outsize = BUFSIZ;
if (iconv (cd, &inptr, &insize, &outptr, &outsize) == (size_t) (-1))
{
/**
* if error is EINVAL or EILSEQ, conversion must be stoped,
* but if it is E2BIG (not enough space in buffer), we just loop again
*/
if( errno != E2BIG)
{
perror ("error iconv");
return NULL;
}
}
length += outptr - tmpbuf;
}
outptr = tmpbuf;
outsize = BUFSIZ;
if (iconv (cd, NULL, NULL, &outptr, &outsize) == (size_t) (-1))
{
perror ("error iconv");
return NULL;
}
length += outptr - tmpbuf;
}
/* length determined, allocate result space */
if ((result = (char*) malloc (length * sizeof (char))) == NULL)
{
perror ("error malloc");
return NULL;
}
/* Do the conversion for real. */
iconv (cd, NULL, NULL, NULL, &dummy);
{
inptr = (char*) input;
insize = inputsize;
outptr = result;
outsize = length;
while (insize > 0)
{
if (iconv (cd, &inptr, &insize, &outptr, &outsize) == (size_t) (-1))
{
if (errno != E2BIG)
{
perror ("error iconv");
free (result);
return NULL;
}
}
}
if (iconv (cd, NULL, NULL, &outptr, &outsize) == (size_t) (-1))
{
perror ("error iconv");
free (result);
return NULL;
}
if (outsize != 0)
abort ();
}
return result;
#else
return strdup (input);
#endif
}

31
src/util_iconv.h Normal file
View File

@ -0,0 +1,31 @@
/*
* util_iconv.h : GeeXboX uShare iconv string encoding utilities headers.
* Originally developped for the GeeXboX project.
* Copyright (C) 2005-2007 Alexis Saettler <asbin@asbin.org>
*
* 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 Library 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef _UTIL_ICONV_H_
#define _UTIL_ICONV_H_
void setup_iconv (void);
void finish_iconv (void);
char *iconv_convert (const char *inbuf)
__attribute__ ((malloc, nonnull, format_arg (1)));
#define UTF8 "UTF-8"
#endif