commit da66e9a2ed2900d4967b65e750926bd7b37e9921 Author: Jean-François GUILLAUME Date: Tue Jan 22 13:13:08 2019 +0100 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f5ad2f8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Jeff MrBear + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6f9bab7 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ + +# gitlab-ldap-sync + +Python project to sync ics calendars into nextcloud. + +Adapted from https://github.com/buzz/ics2owncloud.py . + + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. + +### Prerequisites + +This project has been tested on CentOS 7.6 with nextcloud 14.x, google calendar and zimbra calendar. + +``` +Python : 3.4.9 +pip3 : 8.1.2 +requests : 2.9.1 +icalendar : 3.9.2 +``` + +### Installing + +You could either install requirements system wide or use virtual environment / conda, choose your poison. + +To get this up and running you just need to do the following : + +* Clone the repo +```bash +git clone https://github.com/MrBE4R/nextcloud_ics_sync.git +``` +* Install requirements +```bash +pip3 install -r ./nextcloud_ics_sync/requirements.txt +``` +* Edit config.json with you values +```bash +cp nextcloud_ics_sync/nextcloud_ics_sync.ini{.example,} +EDITOR ./nextcloud_ics_sync/nextcloud_ics_sync.ini +``` +* Start the script and enjoy your calendars being synced +```bash +cd ./nextcloud_ics_sync && ./nextcloud_ics_sync.py +``` + +You should get something like this : +```bash +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@gmail.com (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@gmail.com (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@gmail.com (204) +Imported: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@gmail.com (204) +``` + +You could add the script in a cron to run it periodically. +## Deployment + +How to configure config.json +```ini +[DEFAULT] # The nextcloud server where we will sync all calendar +username: username # Nextcloud username +password: password # Nextcloud password +server: https://nextcloud.example.com/ # Nextcloud URL + +[import_a] # a convenient name for you +calendar: calendar_xy # name of the calendar in nextcloud. must exist prior sync +ics_url: https://cloud.owncloud.org/index.php/apps/calendar/export.php?calid=6 # url to the ics file +ics_username : # username of the distant ics server +ics_password : # password of the distant ics server + +[import_b] +calendar: zimbra +ics_url: https://zimbra.example.com/home/username/Calendar?fmt=ics +ics_username : +ics_password : + +[import_c] +calendar: google +ics_url: https://calendar.google.com/calendar/ical/username/private-xxxxxxx/basic.ics +ics_username : +ics_password : +``` +For now ```ics_username``` and ```ics_password``` are mandatory in each import, meaning the keys should be here but empty if not needed by the distant ics server. + +```create_user``` If set to true, the script will create the users in gitlab and add them in the corresponding groups. Be aware that gitlab will send a mail to every new users created. +## TODO + +- [ ] validate if ```ics_username``` and ```ics_password``` are in config to avoid putting empty value in config +- [ ] your suggestions +## Built With + +* [Python](https://www.python.org/) +* [icalendar](https://icalendar.readthedocs.io/en/latest/) +* [requests](http://docs.python-requests.org/en/latest/) + +## Contributing + +Please read [CONTRIBUTING.md](https://gist.github.com/PurpleBooth/b24679402957c63ec426) for details on our code of conduct, and the process for submitting pull requests to us. + +## Authors + +* **Jean-François GUILLAUME (Jeff MrBear)** - *Initial work* - [MrBE4R](https://github.com/MrBE4R) + +See also the list of [contributors](https://github.com/MrBE4R/nextcloud_ics_sync/contributors) who participated in this project. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details + +## Acknowledgments + +* Hat tip to anyone whose code was used \ No newline at end of file diff --git a/nextcloud_ics_sync.ini.example b/nextcloud_ics_sync.ini.example new file mode 100644 index 0000000..1e8eb42 --- /dev/null +++ b/nextcloud_ics_sync.ini.example @@ -0,0 +1,10 @@ +[DEFAULT] +username: username +password: password +server: https://nextcloud.example.com/ + +[import_a] +calendar: calendar_xy +ics_url: https://nextcloud.example.org/index.php/apps/calendar/export.php?calid=6 +ics_username : +ics_password : diff --git a/nextcloud_ics_sync.py b/nextcloud_ics_sync.py new file mode 100755 index 0000000..fc8a9c5 --- /dev/null +++ b/nextcloud_ics_sync.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +import configparser, logging, requests, traceback +from icalendar.cal import Calendar + +log_option = { + 'format': '[%(asctime)s] [%(levelname)s] %(message)s', + 'level': getattr(logging, 'INFO') +} +logging.basicConfig(**log_option) +logging.getLogger("requests").setLevel(logging.WARNING) + +CALDAVURL = '%sremote.php/dav/calendars/%s/%s' + + +def do_import(username, password, calendar, server, ics_url, ics_username, ics_password): + logging.info(' Working with calendar %s...' % calendar) + base_url = CALDAVURL % (server, username, calendar) + + target_fetch_url = '%s?export' % base_url + r = requests.get(target_fetch_url, auth=(username, password)) + r.raise_for_status() + try: + target_cal = Calendar.from_ical(r.text) + except ValueError as e: + logging.error(' Warning: Could not parse iCal (%s)' % target_fetch_url) + logging.error(e) + return + + existing_uids = [e['UID'].to_ical() for e in target_cal.walk('VEVENT')] + + sourceRequest = requests.get(ics_url, auth=(ics_username, ics_password)) + sourceRequest.encoding = 'utf-8' + sourceContent = sourceRequest.text + c = Calendar.from_ical(sourceContent) + + imported_uids = [] + for e in c.walk('VEVENT'): + uid = bytes.decode(e['UID'].to_ical()) + uid.replace('\'', '') + uid.replace('/', 'slash') + name = bytes.decode(e['SUMMARY'].to_ical()) + cal = Calendar() + cal.add_component(e) + r = requests.put('%s/%s.ics' % (base_url, uid), + data=cal.to_ical(), + auth=(username, password), + headers={'content-type': 'text/calendar; charset=UTF-8'} + ) + if r.status_code == 500 and r'Sabre\VObject\Recur\NoInstancesException' in r.text: + logging.warning(' No valid instances: %s (%s)' % (uid, name)) + elif r.status_code == 201 or r.status_code == 204: + logging.info(' Imported: %s (%s)' % (uid, name)) + imported_uids.append(uid) + else: + r.raise_for_status() + + for uid in existing_uids: + if not uid in imported_uids: + r = requests.delete('%s/%s.ics' % (base_url, uid), auth=(username, password)) + if r.status_code == 204: + logging.info('Deleted %s (%s)' % (uid, name)) + elif r.status_code == 404: + pass + else: + r.raise_for_status() + logging.info(' Done.') + + +if __name__ == '__main__': + logging.info('Initializing script...') + Config = configparser.RawConfigParser() + Config.read('nextcloud_ics_sync.ini') + logging.info('Done.') + logging.info('Importing calendars...') + for key in Config.sections(): + logging.info(' Working on section %s' % key) + try: + do_import( + Config.get(key, 'username'), Config.get(key, 'password'), Config.get(key, 'calendar'), Config.get(key, 'server'), + Config.get(key, 'ics_url'), Config.get(key, 'ics_username'), Config.get(key, 'ics_password') + ) + except Exception as e: + logging.error(traceback.print_exc()) + logging.info(' Done.') + logging.info('Done.') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e67a047 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests==2.9.1 +icalendar==3.9.2