[DEV] import first version 1.1a
This commit is contained in:
commit
389dae52b6
20
AUTHORS
Normal file
20
AUTHORS
Normal 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
340
COPYING
Normal 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
198
ChangeLog
Normal 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 & => &)
|
||||
(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
60
Makefile
Normal 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
108
NEWS
Normal 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 & => &).
|
||||
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
209
README
Normal 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
8
THANKS
Normal 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
3
TODO
Normal 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
740
configure
vendored
Executable 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
89
po/Makefile
Normal 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
4
po/POTFILES
Normal file
@ -0,0 +1,4 @@
|
||||
src/ushare.c
|
||||
src/metadata.c
|
||||
src/cfgparser.c
|
||||
src/presentation.c
|
308
po/de.po
Normal file
308
po/de.po
Normal 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 ..."
|
307
po/fr.po
Normal file
307
po/fr.po
Normal 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
293
po/ushare.pot
Normal 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
26
scripts/Makefile
Normal 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
82
scripts/ushare
Executable 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
45
scripts/ushare.conf
Normal 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
87
src/Makefile
Normal 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
106
src/buffer.c
Normal 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
38
src/buffer.h
Normal 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
872
src/cds.c
Normal 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
212
src/cds.h
Normal 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
450
src/cfgparser.c
Normal 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
56
src/cfgparser.h
Normal 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
180
src/cms.c
Normal 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
166
src/cms.h
Normal 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
93
src/content.c
Normal 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
36
src/content.h
Normal 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
897
src/ctrl_telnet.c
Normal 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
75
src/ctrl_telnet.h
Normal 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
81
src/gettext.h
Normal 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
414
src/http.c
Normal 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
30
src/http.h
Normal 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
595
src/metadata.c
Normal 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[] = {
|
||||
{'"' , """},
|
||||
{'&' , "&"},
|
||||
{'\'', "'"},
|
||||
{'<' , "<"},
|
||||
{'>' , ">"},
|
||||
{'\n', "
"},
|
||||
{'\r', "
"},
|
||||
{'\t', "	"},
|
||||
{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
60
src/metadata.h
Normal 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
150
src/mime.c
Normal 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
32
src/mime.h
Normal 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
32
src/minmax.h
Normal 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
92
src/msr.c
Normal 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
121
src/msr.h
Normal 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
91
src/osdep.c
Normal 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
30
src/osdep.h
Normal 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
215
src/presentation.c
Normal 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
31
src/presentation.h
Normal 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
1140
src/redblack.c
Normal file
File diff suppressed because it is too large
Load Diff
188
src/redblack.h
Normal file
188
src/redblack.h
Normal 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
177
src/services.c
Normal 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
53
src/services.h
Normal 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
64
src/trace.c
Normal 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
55
src/trace.h
Normal 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
121
src/ushare.1
Normal 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
884
src/ushare.c
Normal 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
132
src/ushare.h
Normal 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
170
src/util_iconv.c
Normal 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
31
src/util_iconv.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user