diff --git a/UPnP/.settings/org.eclipse.core.resources.prefs b/UPnP/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/UPnP/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/UPnP/.settings/org.eclipse.jdt.ui.prefs b/UPnP/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..000a7d3 --- /dev/null +++ b/UPnP/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,131 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=false +cleanup.format_source_code=false +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=false +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=false +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup.use_type_arguments=false +cleanup_profile=org.eclipse.jdt.ui.default.eclipse_clean_up_profile +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_PortMapper Formatter +formatter_settings_version=12 +org.eclipse.jdt.ui.exception.name=e +org.eclipse.jdt.ui.gettersetter.use.is=true +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com;\#org.mockito.Mockito; +org.eclipse.jdt.ui.javadoc=false +org.eclipse.jdt.ui.keywordthis=false +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.overrideannotation=true +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=false +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=true +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=false +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_type_arguments=false diff --git a/UPnP/.settings/org.eclipse.m2e.core.prefs b/UPnP/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/UPnP/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/UPnP/build.bat b/UPnP/build.bat new file mode 100644 index 0000000..a19744c --- /dev/null +++ b/UPnP/build.bat @@ -0,0 +1,2 @@ +./gradlew build +java -jar build/libs/portmapper-*.jar \ No newline at end of file diff --git a/UPnP/build.gradle b/UPnP/build.gradle new file mode 100644 index 0000000..bd48452 --- /dev/null +++ b/UPnP/build.gradle @@ -0,0 +1,92 @@ +plugins { + id 'java' + id 'eclipse' + id "com.github.hierynomus.license" version "0.13.1" +} + +repositories { + mavenLocal() + maven { url 'http://4thline.org/m2' } + jcenter() + flatDir { dirs 'lib' } +} + +version = '2.0.0' +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(JavaCompile) { + options.compilerArgs << '-Xlint:all' + options.encoding = 'UTF-8' +} + +test { + if(logger.infoEnabled) { + testLogging.showStandardStreams = true + } + jvmArgs '-XX:+HeapDumpOnOutOfMemoryError', '-enableassertions' +} + +processResources { + rename(/(\w+)_en.properties/, '$1.properties') +} + +task replaceVersionTokenInTranslations { + doLast { + ant.replace(dir: 'build/resources/main', encoding: 'ISO-8859-1') { + include(name: '**/*.properties') + replacefilter(token: '@VERSION_NUMBER@', value: project.version) + } + } +} + +jar.dependsOn(replaceVersionTokenInTranslations) + +jar { + from { + configurations.compile.collect { + it.isDirectory() ? it : zipTree(it) + } + configurations.runtime.collect { + it.isDirectory() ? it : zipTree(it) + } + } + manifest { attributes 'Main-Class': 'org.chris.portmapper.PortMapperStarter' } +} + +dependencies { + testCompile 'junit:junit:4.12' + testCompile 'org.mockito:mockito-core:2.1.0' + + compile 'args4j:args4j:2.33' + compile 'org.slf4j:slf4j-api:1.7.22' + compile 'ch.qos.logback:logback-classic:1.1.8' + compile 'com.miglayout:miglayout-swing:5.0' + compile 'org.jdesktop.bsaf:bsaf:1.9.2' + compile ':sbbi-upnplib:1.0.4' + compile 'org.fourthline.cling:cling-support:2.1.1' + compile 'org.bitlet:weupnp:0.1.4' + + runtime 'commons-jxpath:commons-jxpath:1.1' // sbbi + compile 'org.slf4j:jul-to-slf4j:1.7.22' +} + +license { + header = file('gradle/license-header.txt') + ext.year = 2015 + ext.name = 'Christoph Pirkl' + ext.email = 'christoph at users.sourceforge.net' +} + +eclipse { + classpath { + downloadSources = true + } + jdt.file { + beforeMerged { jdt -> + File defaultProperties = new File("${rootProject.projectDir}/gradle/defaultEclipseJdtPrefs.properties").absoluteFile + logger.info "Load defaults from $defaultProperties for $project" + jdt.load(defaultProperties) + } + } +} \ No newline at end of file diff --git a/UPnP/config/Build PortMapper.launch b/UPnP/config/Build PortMapper.launch new file mode 100644 index 0000000..fef1950 --- /dev/null +++ b/UPnP/config/Build PortMapper.launch @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/UPnP/config/PortMapper.launch b/UPnP/config/PortMapper.launch new file mode 100644 index 0000000..0b1d412 --- /dev/null +++ b/UPnP/config/PortMapper.launch @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/UPnP/gradle/defaultEclipseJdtPrefs.properties b/UPnP/gradle/defaultEclipseJdtPrefs.properties new file mode 100644 index 0000000..8e57ddb --- /dev/null +++ b/UPnP/gradle/defaultEclipseJdtPrefs.properties @@ -0,0 +1,313 @@ +# Template for org.eclipse.jdt.core.prefs, used by build.gradle + +# The following settings will be overwritten by gradle: +#eclipse.preferences.version=1 +#org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +#org.eclipse.jdt.core.compiler.compliance=1.8 +#org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +#org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +#org.eclipse.jdt.core.compiler.source=1.8 + +# Formatter settings: +eclipse.preferences.version=1 +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true + diff --git a/UPnP/gradle/license-header.txt b/UPnP/gradle/license-header.txt new file mode 100644 index 0000000..4f6175b --- /dev/null +++ b/UPnP/gradle/license-header.txt @@ -0,0 +1,15 @@ +UPnP PortMapper - A tool for managing port forwardings via UPnP +Copyright (C) ${year} ${name} <${email}> + +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 3 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, see . \ No newline at end of file diff --git a/UPnP/gradle/wrapper/gradle-wrapper.jar b/UPnP/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..605c807 Binary files /dev/null and b/UPnP/gradle/wrapper/gradle-wrapper.jar differ diff --git a/UPnP/gradle/wrapper/gradle-wrapper.properties b/UPnP/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..6b3e3cd --- /dev/null +++ b/UPnP/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jun 18 20:27:21 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip diff --git a/UPnP/gradlew b/UPnP/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/UPnP/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/UPnP/gradlew.bat b/UPnP/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/UPnP/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/UPnP/lib/sbbi-upnplib-1.0.4.jar b/UPnP/lib/sbbi-upnplib-1.0.4.jar new file mode 100644 index 0000000..271e919 Binary files /dev/null and b/UPnP/lib/sbbi-upnplib-1.0.4.jar differ diff --git a/UPnP/src/main/java/org/chris/portmapper/CommandLineArguments.java b/UPnP/src/main/java/org/chris/portmapper/CommandLineArguments.java new file mode 100644 index 0000000..13c6467 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/CommandLineArguments.java @@ -0,0 +1,118 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +import org.chris.portmapper.model.Protocol; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.kohsuke.args4j.ParserProperties; + +import static java.util.Arrays.*; + +public class CommandLineArguments { + + @Option(name = "-add", usage = "add pf", depends = { "-internalPort", "-externalPort", + "-protocol" }, forbids = { "-gui", "-delete", "-info", "-list" }) + private boolean addPortMapping; + @Option(name = "-delete", usage = "delete pf", depends = { "-externalPort", + "-protocol" }, forbids = { "-gui", "-add", "-info", "-list" }) + private boolean deletePortMapping; + @Option(name = "-info", usage = "router info", forbids = { "-gui", "-delete", "-add", "-list" }) + private boolean printInfo; + @Option(name = "-list", usage = "print pf", forbids = { "-gui", "-delete", "-info", "-add" }) + private boolean listPortMappings; + + @Option(name = "-ip", usage = "Internal IP of the port mapping (default: localhost)") + private String internalIp; + @Option(name = "-internalPort", usage = "Internal port of the port mapping") + private int internalPort; + @Option(name = "-externalPort", usage = "External port of the port mapping") + private int externalPort; + @Option(name = "-protocol", usage = "Protocol of the port mapping") + private Protocol protocol; + @Option(name = "-description", usage = "Description of the port mapping") + private String description; + + @Option(name = "-lib", usage = "UPnP library") + private String upnpLib; + + @Option(name = "-routerIndex", usage = "router index") + private Integer routerIndex; + + private final CmdLineParser parser; + + public CommandLineArguments() { + parser = new CmdLineParser(this, ParserProperties.defaults().withShowDefaults(true).withUsageWidth(80)); + } + + public boolean parse(final String[] args) { + try { + parser.parseArgument(asList(args)); + return true; + } catch (final CmdLineException e) { + System.err.println(e.getMessage()); + printHelp(); + return false; + } + } + + public boolean isAddPortMapping() { + return addPortMapping; + } + + public boolean isDeletePortMapping() { + return deletePortMapping; + } + + public boolean isPrintInfo() { + return printInfo; + } + + public boolean isListPortMappings() { + return listPortMappings; + } + + public String getInternalIp() { + return internalIp; + } + + public int getInternalPort() { + return internalPort; + } + + public int getExternalPort() { + return externalPort; + } + + public Protocol getProtocol() { + return protocol; + } + + public String getUpnpLib() { + return upnpLib; + } + + public Integer getRouterIndex() { + return routerIndex; + } + + public String getDescription() { + return description; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/MacSetup.java b/UPnP/src/main/java/org/chris/portmapper/MacSetup.java new file mode 100644 index 0000000..1b96a42 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/MacSetup.java @@ -0,0 +1,32 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +/** + * This class contains mac specific settings for the application name and the application menu. + */ +public class MacSetup { + + public static void setupMac() { + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "UPnP PortMapper"); + System.setProperty("apple.laf.useScreenMenuBar", "true"); + System.setProperty("apple.awt.brushMetalLook", "false"); + System.setProperty("com.apple.mrj.application.growbox.intrudes", "false"); + System.setProperty("com.apple.mrj.application.live-resize", "true"); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/PortMapperApp.java b/UPnP/src/main/java/org/chris/portmapper/PortMapperApp.java new file mode 100644 index 0000000..f806099 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/PortMapperApp.java @@ -0,0 +1,389 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Collection; +import java.util.Collections; +import java.util.EventObject; +import java.util.List; + +import javax.swing.JOptionPane; + +import org.chris.portmapper.gui.PortMapperView; +import org.chris.portmapper.logging.LogMessageListener; +import org.chris.portmapper.logging.LogMessageOutputStream; +import org.chris.portmapper.logging.LogbackConfiguration; +import org.chris.portmapper.model.PortMappingPreset; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; +import org.jdesktop.application.ResourceMap; +import org.jdesktop.application.SingleFrameApplication; +import org.jdesktop.application.utils.AppHelper; +import org.jdesktop.application.utils.OSXAdapter; +import org.jdesktop.application.utils.PlatformType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The main application class + */ +public class PortMapperApp extends SingleFrameApplication { + + /** + * The name of the system property which will be used as the directory where all configuration files will be stored. + */ + private static final String CONFIG_DIR_PROPERTY_NAME = "portmapper.config.dir"; + + /** + * The file name for the settings file. + */ + private static final String SETTINGS_FILENAME = "settings.xml"; + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private IRouter router; + private Settings settings; + private final LogMessageOutputStream logMessageOutputStream = new LogMessageOutputStream(); + private final LogbackConfiguration logbackConfig = new LogbackConfiguration(); + + @Override + protected void startup() { + logbackConfig.registerOutputStream(logMessageOutputStream); + + setCustomConfigDir(); + + loadSettings(); + + final PortMapperView view = new PortMapperView(this); + addExitListener(new ExitListener() { + @Override + public boolean canExit(final EventObject arg0) { + return true; + } + + @Override + public void willExit(final EventObject arg0) { + disconnectRouter(); + } + }); + + show(view); + + if (AppHelper.getPlatform() == PlatformType.OS_X) { + registerMacOSXListeners(); + } + } + + private void registerMacOSXListeners() { + final PortMapperView view = getView(); + OSXAdapter.setPreferencesHandler(view, getMethod(PortMapperView.class, "changeSettings")); + OSXAdapter.setAboutHandler(view, getMethod(PortMapperView.class, "showAboutDialog")); + } + + private static Method getMethod(final Class clazz, final String name, final Class... parameterTypes) { + try { + return clazz.getMethod(name, parameterTypes); + } catch (final SecurityException e) { + throw new RuntimeException("Error getting method " + name + " of class " + clazz.getName(), e); + } catch (final NoSuchMethodException e) { + throw new RuntimeException("Error getting method " + name + " of class " + clazz.getName(), e); + } + } + + /** + * Read the system property with name {@link PortMapperApp#CONFIG_DIR_PROPERTY_NAME} and change the local storage + * directory if the property is given and points to a writable directory. If there is a directory named + * PortMapperConf in the current directory, use this as the configuration directory. + */ + private void setCustomConfigDir() { + final String customConfigurationDir = System.getProperty(CONFIG_DIR_PROPERTY_NAME); + final File portableAppConfigDir = new File("PortMapperConf"); + + // the property is set: check, if the given directory can be used + if (customConfigurationDir != null) { + final File dir = new File(customConfigurationDir); + if (!dir.isDirectory()) { + logger.error("Custom configuration directory '{}' is not a directory.", customConfigurationDir); + System.exit(1); + } + if (!dir.canRead() || !dir.canWrite()) { + logger.error("Can not read or write to custom configuration directory '{}'.", customConfigurationDir); + System.exit(1); + } + logger.info("Using custom configuration directory '{}'.", dir.getAbsolutePath()); + getContext().getLocalStorage().setDirectory(dir); + + // check, if the portable app directory exists and use this one + } else if (portableAppConfigDir.isDirectory() && portableAppConfigDir.canRead() + && portableAppConfigDir.canWrite()) { + logger.info("Found portable app configuration directory '{}'.", portableAppConfigDir.getAbsolutePath()); + getContext().getLocalStorage().setDirectory(portableAppConfigDir); + + // use the default configuration directory + } else { + logger.info("Using default configuration directory '{}'.", + getContext().getLocalStorage().getDirectory().getAbsolutePath()); + } + } + + /** + * Load the application settings from file {@link PortMapperApp#SETTINGS_FILENAME} located in the configuration + * directory. + */ + private void loadSettings() { + logger.debug("Loading settings from file {}", SETTINGS_FILENAME); + try { + settings = (Settings) getContext().getLocalStorage().load(SETTINGS_FILENAME); + } catch (final IOException e) { + logger.warn("Could not load settings from file " + SETTINGS_FILENAME, e); + } + + if (settings == null) { + logger.debug("Settings were not loaded from file {}: create new settings", SETTINGS_FILENAME); + settings = new Settings(); + } else { + logger.debug("Got settings {}", settings); + this.setLogLevel(settings.getLogLevel()); + } + } + + public void setLogMessageListener(final LogMessageListener listener) { + this.logMessageOutputStream.registerListener(listener); + } + + @Override + protected void shutdown() { + super.shutdown(); + logger.debug("Saving settings {} to file {}", settings, SETTINGS_FILENAME); + if (logger.isTraceEnabled()) { + for (final PortMappingPreset preset : settings.getPresets()) { + logger.trace("Saving port mapping {}", preset.getCompleteDescription()); + } + } + try { + getContext().getLocalStorage().save(settings, SETTINGS_FILENAME); + } catch (final IOException e) { + logger.warn("Could not save settings to file " + SETTINGS_FILENAME, e); + } + } + + public ResourceMap getResourceMap() { + return getContext().getResourceMap(); + } + + public PortMapperView getView() { + return (PortMapperView) getMainView(); + } + + public void connectRouter() throws RouterException { + if (this.router != null) { + logger.warn("Already connected to router. Cannot create a second connection."); + return; + } + + final AbstractRouterFactory routerFactory; + try { + routerFactory = createRouterFactory(); + } catch (final RouterException e) { + logger.error("Could not create router factory: " + e.getMessage(), e); + return; + } + logger.info("Searching for routers..."); + + final Collection foundRouters = routerFactory.findRouters(); + + // No routers found + if (foundRouters == null || foundRouters.size() == 0) { + throw new RouterException("Did not find a router"); + } + + // One router found: use it. + if (foundRouters.size() == 1) { + router = foundRouters.iterator().next(); + logger.info("Connected to router '{}'", router.getName()); + this.getView().fireConnectionStateChange(); + return; + } + + // More than one router found: ask user. + logger.info("Found more than one router (count: {}): ask user.", foundRouters.size()); + + final ResourceMap resourceMap = getResourceMap(); + final IRouter selectedRouter = (IRouter) JOptionPane.showInputDialog(this.getView().getFrame(), + resourceMap.getString("messages.select_router.message"), + resourceMap.getString("messages.select_router.title"), JOptionPane.QUESTION_MESSAGE, null, + foundRouters.toArray(), null); + + if (selectedRouter == null) { + logger.info("No router selected."); + return; + } + + this.router = selectedRouter; + this.getView().fireConnectionStateChange(); + } + + private AbstractRouterFactory createRouterFactory() throws RouterException { + logger.info("Creating router factory for class {}", settings.getRouterFactoryClassName()); + final Class routerFactoryClass = getClassForName(settings.getRouterFactoryClassName()); + final Constructor constructor = getConstructor(routerFactoryClass); + final AbstractRouterFactory routerFactory = createInstance(constructor); + logger.debug("Router factory {} created", routerFactory); + return routerFactory; + } + + private AbstractRouterFactory createInstance(final Constructor constructor) + throws RouterException { + try { + return constructor.newInstance(this); + } catch (final Exception e) { + throw new RouterException("Could not create a router factory using constructor " + constructor, e); + } + } + + private static Constructor getConstructor(final Class clazz) + throws RouterException { + try { + return clazz.getConstructor(PortMapperApp.class); + } catch (final NoSuchMethodException e) { + throw new RouterException("Could not find constructor of " + clazz.getName(), e); + } catch (final SecurityException e1) { + throw new RouterException("Could not find constructor of " + clazz.getName(), e1); + } + } + + @SuppressWarnings("unchecked") + private static Class getClassForName(final String className) throws RouterException { + try { + return (Class) Class.forName(className); + } catch (final ClassNotFoundException e) { + throw new RouterException("Did not find router factory class for name " + className, e); + } + } + + public boolean disconnectRouter() { + if (this.router == null) { + logger.warn("Not connected to router. Can not disconnect."); + return false; + } + + this.router.disconnect(); + this.router = null; + this.getView().fireConnectionStateChange(); + return true; + } + + public IRouter getRouter() { + return router; + } + + public Settings getSettings() { + return settings; + } + + public boolean isConnected() { + return this.getRouter() != null; + } + + /** + * Get the IP address of the local host. + * + * @return IP address of the local host or null, if the address could not be determined. + * @throws RouterException + */ + public String getLocalHostAddress() { + + try { + if (router != null) { + logger.debug("Connected to router, get IP of localhost from socket..."); + return router.getLocalHostAddress(); + } + + logger.debug("Not connected to router, get IP of localhost from network interface..."); + final InetAddress address = getLocalhostAddressFromNetworkInterface(); + if (address != null) { + return address.getHostAddress(); + } else { + logger.warn("Did not get IP of localhost from network interface"); + } + + } catch (final RouterException e) { + logger.warn("Could not get address of localhost.", e); + logger.warn("Could not get address of localhost. Please enter it manually."); + } + return null; + } + + private InetAddress getLocalhostAddressFromNetworkInterface() throws RouterException { + try { + final List networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); + logger.trace("Found network interfaces " + networkInterfaces); + for (final NetworkInterface nInterface : networkInterfaces) { + if (nInterface.isLoopback()) { + logger.debug("Found loopback network interface " + nInterface.getDisplayName() + "/" + + nInterface.getName() + " with IPs " + nInterface.getInterfaceAddresses() + ": ignore."); + } else if (!nInterface.isUp()) { + logger.debug("Found inactive network interface " + nInterface.getDisplayName() + "/" + + nInterface.getName() + " with IPs " + nInterface.getInterfaceAddresses() + ": ignore."); + } else { + logger.debug("Found network interface " + nInterface.getDisplayName() + "/" + nInterface.getName() + + " with IPs " + nInterface.getInterfaceAddresses() + ": use this one."); + final List addresses = Collections.list(nInterface.getInetAddresses()); + if (addresses.size() > 0) { + final InetAddress address = findIPv4Adress(nInterface, addresses); + logger.debug("Found one address for network interface " + nInterface.getName() + ": using " + + address); + return address; + } + logger.debug("Network interface " + nInterface.getName() + " has no addresses."); + } + } + } catch (final SocketException e) { + throw new RouterException("Did not get network interfaces.", e); + } + return null; + } + + private InetAddress findIPv4Adress(final NetworkInterface nInterface, final List addresses) { + if (addresses.size() == 1) { + return addresses.get(0); + } + + for (final InetAddress inetAddress : addresses) { + if (inetAddress.getHostAddress().contains(".")) { + logger.debug("Found IPv4 address " + inetAddress); + return inetAddress; + } + } + final InetAddress address = addresses.get(0); + logger.info("Found more than one address for network interface " + nInterface.getName() + ": using " + address); + return address; + } + + public void setLogLevel(final String logLevel) { + this.logbackConfig.setLogLevel(logLevel); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/PortMapperCli.java b/UPnP/src/main/java/org/chris/portmapper/PortMapperCli.java new file mode 100644 index 0000000..7691cf2 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/PortMapperCli.java @@ -0,0 +1,238 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; +import org.chris.portmapper.router.cling.ClingRouterFactory; +import org.jdesktop.application.Application; +import org.jdesktop.application.utils.AppHelper; +import org.jdesktop.application.utils.PlatformType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PortMapperCli { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final CommandLineArguments cmdLineArgs; + private String routerFactoryClassName = ClingRouterFactory.class.getName(); + private Integer routerIndex = null; + + public PortMapperCli() { + cmdLineArgs = new CommandLineArguments(); + } + + public void start(final String[] args) { + if (!cmdLineArgs.parse(args)) { + System.exit(1); + } + if (isStartGuiRequired()) { + startGui(args); + return; + } + + if (cmdLineArgs.getUpnpLib() != null) { + this.routerFactoryClassName = cmdLineArgs.getUpnpLib(); + logger.info("Using router factory class '" + this.routerFactoryClassName + "'"); + } + + if (cmdLineArgs.getRouterIndex() != null) { + this.routerIndex = cmdLineArgs.getRouterIndex(); + logger.info("Using router index " + this.routerIndex); + } + + if (cmdLineArgs.isPrintHelp()) { + printHelp(); + return; + } + try { + final IRouter router = connect(); + if (router == null) { + logger.error("No router found: exit"); + System.exit(1); + return; + } + if (cmdLineArgs.isAddPortMapping()) { + addPortForwarding(router); + } else if (cmdLineArgs.isPrintInfo()) { + printStatus(router); + } else if (cmdLineArgs.isDeletePortMapping()) { + deletePortForwardings(router); + } else if (cmdLineArgs.isListPortMappings()) { + printPortForwardings(router); + } else { + router.disconnect(); + System.err.println("Incorrect usage"); + printHelp(); + System.exit(1); + return; + } + router.disconnect(); + } catch (final RouterException e) { + logger.error("An error occured", e); + System.exit(1); + return; + } + System.exit(0); + } + + private void startGui(final String[] args) { + if (AppHelper.getPlatform() == PlatformType.OS_X) { + MacSetup.setupMac(); + } + Application.launch(PortMapperApp.class, args); + } + + private void printPortForwardings(final IRouter router) throws RouterException { + final Collection mappings = router.getPortMappings(); + if (mappings.size() == 0) { + logger.info("No port mappings found"); + return; + } + final StringBuilder b = new StringBuilder(); + for (final Iterator iterator = mappings.iterator(); iterator.hasNext();) { + final PortMapping mapping = iterator.next(); + b.append(mapping.getCompleteDescription()); + if (iterator.hasNext()) { + b.append("\n"); + } + } + logger.info("Found " + mappings.size() + " port forwardings:\n" + b.toString()); + } + + private void deletePortForwardings(final IRouter router) throws RouterException { + + final String remoteHost = null; + final int port = cmdLineArgs.getExternalPort(); + final Protocol protocol = cmdLineArgs.getProtocol(); + logger.info("Deleting mapping for protocol " + protocol + " and external port " + port); + router.removePortMapping(protocol, remoteHost, port); + printPortForwardings(router); + } + + private void printStatus(final IRouter router) throws RouterException { + router.logRouterInfo(); + } + + private void addPortForwarding(final IRouter router) throws RouterException { + + final String remoteHost = null; + final String internalClient = cmdLineArgs.getInternalIp() != null ? cmdLineArgs.getInternalIp() + : router.getLocalHostAddress(); + final int internalPort = cmdLineArgs.getInternalPort(); + final int externalPort = cmdLineArgs.getExternalPort(); + final Protocol protocol = cmdLineArgs.getProtocol(); + + final String description = cmdLineArgs.getDescription() != null ? cmdLineArgs.getDescription() + : "PortMapper " + protocol + "/" + internalClient + ":" + internalPort; + final PortMapping mapping = new PortMapping(protocol, remoteHost, externalPort, internalClient, internalPort, + description); + logger.info("Adding mapping " + mapping); + router.addPortMapping(mapping); + printPortForwardings(router); + } + + private void printHelp() { + cmdLineArgs.printHelp(); + } + + private boolean isStartGuiRequired() { + if (cmdLineArgs.isStartGui()) { + return true; + } + return !(cmdLineArgs.isPrintHelp() || cmdLineArgs.isAddPortMapping() || cmdLineArgs.isPrintInfo() + || cmdLineArgs.isListPortMappings() || cmdLineArgs.isDeletePortMapping()); + } + + @SuppressWarnings("unchecked") + private AbstractRouterFactory createRouterFactory() throws RouterException { + Class routerFactoryClass; + logger.info("Creating router factory for class {}", routerFactoryClassName); + try { + routerFactoryClass = (Class) Class.forName(routerFactoryClassName); + } catch (final ClassNotFoundException e) { + throw new RouterException("Did not find router factory class for name " + routerFactoryClassName, e); + } + + logger.debug("Creating a new instance of the router factory class {}", routerFactoryClass); + try { + final Constructor constructor = routerFactoryClass + .getConstructor(PortMapperApp.class); + return constructor.newInstance(new PortMapperApp()); + } catch (final Exception e) { + throw new RouterException("Error creating a router factory using class " + routerFactoryClass.getName(), e); + } + } + + private IRouter connect() throws RouterException { + AbstractRouterFactory routerFactory; + try { + routerFactory = createRouterFactory(); + } catch (final RouterException e) { + logger.error("Could not create router factory", e); + return null; + } + logger.info("Searching for routers..."); + + final List foundRouters = routerFactory.findRouters(); + + return selectRouter(foundRouters); + } + + /** + * @param foundRouters + * @return + */ + private IRouter selectRouter(final List foundRouters) { + // One router found: use it. + if (foundRouters.size() == 1) { + final IRouter router = foundRouters.iterator().next(); + logger.info("Connected to router " + router.getName()); + return router; + } else if (foundRouters.size() == 0) { + logger.error("Found no router"); + return null; + } else if (foundRouters.size() > 1 && routerIndex == null) { + // let user choose which router to use. + logger.error("Found more than one router. Use option -i "); + + int index = 0; + for (final IRouter iRouter : foundRouters) { + logger.error("- index " + index + ": " + iRouter.getName()); + index++; + } + return null; + } else if (routerIndex >= 0 && routerIndex < foundRouters.size()) { + final IRouter router = foundRouters.get(routerIndex); + logger.info("Found more than one router, using " + router.getName()); + return router; + } else { + logger.error("Index must be between 0 and " + (foundRouters.size() - 1)); + return null; + } + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/PortMapperStarter.java b/UPnP/src/main/java/org/chris/portmapper/PortMapperStarter.java new file mode 100644 index 0000000..5ec8e7f --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/PortMapperStarter.java @@ -0,0 +1,43 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; + +public class PortMapperStarter { + + private final static Logger LOG = LoggerFactory.getLogger(PortMapperStarter.class); + + public static void main(final String[] args) { + redirectJavaUtilLoggingToLogback(); + final PortMapperCli cli = new PortMapperCli(); + try { + cli.start(args); + } catch (final Exception e) { + LOG.error("PortMapper failed with exception " + e.getMessage(), e); + System.exit(1); + } + } + + private static void redirectJavaUtilLoggingToLogback() { + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/Settings.java b/UPnP/src/main/java/org/chris/portmapper/Settings.java new file mode 100644 index 0000000..e92abc3 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/Settings.java @@ -0,0 +1,112 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.chris.portmapper.model.PortMappingPreset; +import org.chris.portmapper.router.cling.ClingRouterFactory; + +import ch.qos.logback.classic.Level; + +public class Settings implements Serializable { + + private static final long serialVersionUID = -1349121864190290050L; + + public final static String PROPERTY_PORT_MAPPING_PRESETS = "presets"; + + private List presets; + private boolean useEntityEncoding; + private String logLevel; + private String routerFactoryClassName; + + private transient PropertyChangeSupport propertyChangeSupport; + + public Settings() { + useEntityEncoding = true; + logLevel = Level.INFO.toString(); + presets = new ArrayList<>(); + routerFactoryClassName = ClingRouterFactory.class.getName(); + propertyChangeSupport = new PropertyChangeSupport(this); + } + + public void addPropertyChangeListener(final String property, final PropertyChangeListener listener) { + this.propertyChangeSupport.addPropertyChangeListener(property, listener); + } + + public List getPresets() { + return presets; + } + + public void setPresets(final List presets) { + this.presets = presets; + } + + public void addPreset(final PortMappingPreset newPreset) { + final List oldPresets = new ArrayList<>(this.presets); + this.presets.add(newPreset); + this.propertyChangeSupport.firePropertyChange(PROPERTY_PORT_MAPPING_PRESETS, oldPresets, + new ArrayList<>(this.presets)); + } + + public void removePresets(final PortMappingPreset selectedPreset) { + final List oldPresets = new ArrayList<>(this.presets); + this.presets.remove(selectedPreset); + this.propertyChangeSupport.firePropertyChange(PROPERTY_PORT_MAPPING_PRESETS, oldPresets, + new ArrayList<>(this.presets)); + } + + public void savePreset(final PortMappingPreset portMappingPreset) { + this.propertyChangeSupport.firePropertyChange(PROPERTY_PORT_MAPPING_PRESETS, null, + new ArrayList<>(this.presets)); + } + + @Override + public String toString() { + return "[Settings: presets=" + presets + ", useEntityEncoding=" + useEntityEncoding + ", logLevel=" + logLevel + + ", routerFactoryClassName=" + routerFactoryClassName + "]"; + } + + public boolean isUseEntityEncoding() { + return useEntityEncoding; + } + + public void setUseEntityEncoding(final boolean useEntityEncoding) { + this.useEntityEncoding = useEntityEncoding; + } + + public String getLogLevel() { + return this.logLevel; + } + + public void setLogLevel(final String logLevel) { + this.logLevel = logLevel; + } + + public String getRouterFactoryClassName() { + return routerFactoryClassName; + } + + public void setRouterFactoryClassName(final String routerFactoryClassName) { + this.routerFactoryClassName = routerFactoryClassName; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageListener.java b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageListener.java new file mode 100644 index 0000000..2c5adbc --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageListener.java @@ -0,0 +1,36 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.logging; + +import javax.swing.JTextArea; + +/** + * The {@link LogMessageWriter} copies every written string to a {@link LogMessageListener}. All written strings are + * buffered, so no string is missed. A {@link LogMessageListener} can be registered using method + * {@link #registerListener(JTextArea)}. + */ +public interface LogMessageListener { + + /** + * Process the given log message. This could mean e.g. to display the message to the user. + * + * @param message + * the message to process. + */ + public void addLogMessage(String message); +} diff --git a/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageOutputStream.java b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageOutputStream.java new file mode 100644 index 0000000..e383c90 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageOutputStream.java @@ -0,0 +1,104 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.logging; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.List; + +import javax.swing.JTextArea; + +/** + * This class implements an output stream that appends to a list and allows listeners to register for written lines. + */ +public class LogMessageOutputStream extends OutputStream { + + /** + * The listener to which the strings will be forwarded. + */ + private LogMessageListener logListener; + + /** + * The buffer to which the written strings are added until a listener is registered. + */ + private List unprocessedMessagesBuffer; + + /** + * Creates a new {@link LogMessageOutputStream}. At creation time, no listener is registered, so that all added text + * is stored in a buffer. + */ + public LogMessageOutputStream() { + unprocessedMessagesBuffer = new LinkedList<>(); + } + + @Override + public void close() { + // ignore + } + + @Override + public void flush() { + // ignore + } + + @Override + public void write(final byte b[], final int off, final int len) throws IOException { + final String line = new String(b, off, len); + addMessage(line); + } + + @Override + public void write(final int b) throws IOException { + throw new UnsupportedOperationException(); + } + + /** + * Append the given message to the registered {@link LogMessageListener}. If no listener is registered, the string + * is written to a buffer. When a listener is registered, the buffered text will be appended to the listener. + * + * @param message + * the message to append. + */ + public void addMessage(final String message) { + if (this.logListener != null) { + this.logListener.addLogMessage(message); + } else { + unprocessedMessagesBuffer.add(message); + } + } + + /** + * Registers a {@link JTextArea}, so that all strings written to this writer are appended to the given text area. + * After registration, all buffered strings are appended to the text area, so that no string is missed. + * + * @param textArea + * the text area to wich to append the strings. + */ + public void registerListener(final LogMessageListener textArea) { + this.logListener = textArea; + + // append the buffered text to the text area. + for (final String line : unprocessedMessagesBuffer) { + this.logListener.addLogMessage(line); + } + // we do not need the buffer any more, all text will be appended + // to the text area. + this.unprocessedMessagesBuffer = null; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageWriter.java b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageWriter.java new file mode 100644 index 0000000..97dec3c --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/logging/LogMessageWriter.java @@ -0,0 +1,100 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.logging; + +import java.io.Writer; +import java.util.LinkedList; +import java.util.List; + +import javax.swing.JTextArea; + +/** + * The {@link LogMessageWriter} copies every written string to a {@link LogMessageListener}. All written strings are + * buffered, so no string is missed. A {@link LogMessageListener} can be registered using method + * {@link #registerListener(JTextArea)}. + */ +public class LogMessageWriter extends Writer { + + /** + * The listener to which the strings will be forwarded. + */ + private LogMessageListener logListener; + + /** + * The buffer to which the written strings are added until a listener is registered. + */ + private List unprocessedMessagesBuffer; + + /** + * Creates a new {@link LogMessageWriter}. At creation time, no listener is registered, so that all added text is + * stored in a buffer. + */ + public LogMessageWriter() { + unprocessedMessagesBuffer = new LinkedList<>(); + } + + @Override + public void close() { + // ignore + } + + @Override + public void flush() { + // ignore + } + + @Override + public void write(final char[] cbuf, final int off, final int len) { + final String line = new String(cbuf, off, len); + addMessage(line); + } + + /** + * Append the given message to the registered {@link LogMessageListener}. If no listener is registered, the string + * is written to a buffer. When a listener is registered, the buffered text will be appended to the listener. + * + * @param message + * the message to append. + */ + public void addMessage(final String message) { + if (this.logListener != null) { + this.logListener.addLogMessage(message); + } else { + unprocessedMessagesBuffer.add(message); + } + } + + /** + * Registers a {@link JTextArea}, so that all strings written to this writer are appended to the given text area. + * After registration, all buffered strings are appended to the text area, so that no string is missed. + * + * @param textArea + * the text area to wich to append the strings. + */ + public void registerListener(final LogMessageListener textArea) { + this.logListener = textArea; + + // append the buffered text to the text area. + for (final String line : unprocessedMessagesBuffer) { + this.logListener.addLogMessage(line); + } + // we do not need the buffer any more, all text will be appended + // to the text area. + this.unprocessedMessagesBuffer = null; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/logging/LogbackConfiguration.java b/UPnP/src/main/java/org/chris/portmapper/logging/LogbackConfiguration.java new file mode 100644 index 0000000..0867485 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/logging/LogbackConfiguration.java @@ -0,0 +1,83 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.logging; + +import java.io.OutputStream; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.OutputStreamAppender; +import ch.qos.logback.core.encoder.Encoder; + +public class LogbackConfiguration { + + private static final String PATTERN_LAYOUT = "%-5level %msg%n"; + private static final String OUTPUT_STREAM_APPENDER_NAME = "OUTPUT_STREAM"; + private static final String LOGGER_NAME = "ROOT"; + + private final LoggerContext loggerContext; + + public LogbackConfiguration() { + loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + } + + public void registerOutputStream(final OutputStream logMessageOutputStream) { + final Encoder encoder = createPatternLayoutEncoder(PATTERN_LAYOUT); + final OutputStreamAppender appender = createAppender(logMessageOutputStream, encoder); + configureLogger(appender); + } + + private void configureLogger(final OutputStreamAppender appender) { + final Logger logbackLogger = getLogger(); + logbackLogger.addAppender(appender); + logbackLogger.setAdditive(false); + } + + private Logger getLogger() { + return (Logger) LoggerFactory.getLogger(LOGGER_NAME); + } + + private OutputStreamAppender createAppender(final OutputStream logMessageOutputStream, + final Encoder encoder) { + final OutputStreamAppender appender = new OutputStreamAppender(); + appender.setContext(loggerContext); + appender.setEncoder(encoder); + appender.setOutputStream(logMessageOutputStream); + appender.setName(OUTPUT_STREAM_APPENDER_NAME); + appender.start(); + return appender; + } + + private PatternLayoutEncoder createPatternLayoutEncoder(final String pattern) { + final PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(loggerContext); + encoder.setPattern(pattern); + encoder.start(); + return encoder; + } + + public void setLogLevel(final String logLevel) { + final Level level = Level.toLevel(logLevel); + getLogger().setLevel(level); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/model/PortMapping.java b/UPnP/src/main/java/org/chris/portmapper/model/PortMapping.java new file mode 100644 index 0000000..d970fd6 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/model/PortMapping.java @@ -0,0 +1,159 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.model; + +import java.util.HashMap; +import java.util.Map; + +import net.sbbi.upnp.messages.ActionResponse; + +/** + * This immutable class represents a port mapping / forwarding on a router. + */ +public class PortMapping implements Cloneable { + + public static final String MAPPING_ENTRY_LEASE_DURATION = "NewLeaseDuration"; + public static final String MAPPING_ENTRY_ENABLED = "NewEnabled"; + public static final String MAPPING_ENTRY_REMOTE_HOST = "NewRemoteHost"; + public static final String MAPPING_ENTRY_INTERNAL_CLIENT = "NewInternalClient"; + public static final String MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION = "NewPortMappingDescription"; + public static final String MAPPING_ENTRY_PROTOCOL = "NewProtocol"; + public static final String MAPPING_ENTRY_INTERNAL_PORT = "NewInternalPort"; + public static final String MAPPING_ENTRY_EXTERNAL_PORT = "NewExternalPort"; + + private static final long DEFAULT_LEASE_DURATION = 0; + + private final int externalPort; + private final Protocol protocol; + private final int internalPort; + private final String description; + private final String internalClient; + private final String remoteHost; + private final boolean enabled; + private final long leaseDuration; + + public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort, + final String internalClient, final int internalPort, final String description) { + this(protocol, remoteHost, externalPort, internalClient, internalPort, description, true, + DEFAULT_LEASE_DURATION); + } + + public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort, + final String internalClient, final int internalPort, final String description, final boolean enabled, + final long leaseDuration) { + this.protocol = protocol; + this.remoteHost = remoteHost; + this.externalPort = externalPort; + this.internalClient = internalClient; + this.internalPort = internalPort; + this.description = description; + this.enabled = enabled; + this.leaseDuration = leaseDuration; + } + + private PortMapping(final ActionResponse response) { + final Map values = new HashMap<>(); + + for (final Object argObj : response.getOutActionArgumentNames()) { + final String argName = (String) argObj; + values.put(argName, response.getOutActionArgumentValue(argName)); + } + + externalPort = Integer.parseInt(values.get(MAPPING_ENTRY_EXTERNAL_PORT)); + internalPort = Integer.parseInt(values.get(MAPPING_ENTRY_INTERNAL_PORT)); + final String protocolString = values.get(MAPPING_ENTRY_PROTOCOL); + protocol = (protocolString.equalsIgnoreCase("TCP") ? Protocol.TCP : Protocol.UDP); + description = values.get(MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION); + internalClient = values.get(MAPPING_ENTRY_INTERNAL_CLIENT); + remoteHost = values.get(MAPPING_ENTRY_REMOTE_HOST); + final String enabledString = values.get(MAPPING_ENTRY_ENABLED); + enabled = enabledString != null && enabledString.equals("1"); + leaseDuration = Long.parseLong(values.get(MAPPING_ENTRY_LEASE_DURATION)); + } + + public static PortMapping create(final ActionResponse response) { + final PortMapping mapping = new PortMapping(response); + return mapping; + } + + /** + * @return the leaseDuration + */ + public long getLeaseDuration() { + return leaseDuration; + } + + public int getExternalPort() { + return externalPort; + } + + public Protocol getProtocol() { + return protocol; + } + + public int getInternalPort() { + return internalPort; + } + + public String getDescription() { + return description; + } + + public String getInternalClient() { + return internalClient; + } + + public String getRemoteHost() { + return remoteHost; + } + + public boolean isEnabled() { + return enabled; + } + + public String getCompleteDescription() { + final StringBuilder b = new StringBuilder(); + b.append(protocol); + b.append(" "); + if (remoteHost != null) { + b.append(remoteHost); + } + b.append(":"); + b.append(externalPort); + b.append(" -> "); + b.append(internalClient); + b.append(":"); + b.append(internalPort); + b.append(" "); + b.append(enabled ? "enabled" : "not enabled"); + b.append(" "); + b.append(description); + return b.toString(); + } + + @Override + public String toString() { + return description; + } + + @Override + public Object clone() { + return new PortMapping(protocol, remoteHost, externalPort, internalClient, internalPort, description, enabled, + leaseDuration); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/model/PortMappingPreset.java b/UPnP/src/main/java/org/chris/portmapper/model/PortMappingPreset.java new file mode 100644 index 0000000..97737cc --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/model/PortMappingPreset.java @@ -0,0 +1,173 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.chris.portmapper.Settings; + +/** + * This class stores a port mapping preset containing a description, internal and remote host and a {@link List} of + * {@link SinglePortMapping}s. + */ +public class PortMappingPreset implements Cloneable, Serializable { + + private static final long serialVersionUID = 3749136884938395765L; + + /** + * The description of this preset. + */ + private String description; + + /** + * The ip address of the internal client or null for localhost. + */ + private String internalClient; + + /** + * The host name of the remote host. + */ + private String remoteHost; + + /** + * The port mappings in this preset. + */ + private List ports; + + /** + * true if this preset has not been saved. + */ + private boolean isNew; + + /** + * Creates a new preset with the given default values. + */ + public PortMappingPreset(final String remoteHost, final String internalClient, final String description) { + this.remoteHost = remoteHost; + this.internalClient = internalClient; + this.description = description; + this.ports = new LinkedList<>(); + + this.isNew = false; + } + + /** + * Creates a new empty preset. + */ + public PortMappingPreset() { + this.ports = new LinkedList<>(); + this.isNew = true; + } + + @Override + public String toString() { + return description; + } + + public List getPortMappings(final String localhost) { + if (this.useLocalhostAsInternalClient() && (localhost == null || localhost.length() == 0)) { + throw new IllegalArgumentException("Got invalid localhost and internal host is not given."); + } + + final List allPortMappings = new ArrayList<>(this.ports.size()); + for (final SinglePortMapping port : this.ports) { + final String internalClientName = this.useLocalhostAsInternalClient() ? localhost : this.internalClient; + + final PortMapping newMapping = new PortMapping(port.getProtocol(), remoteHost, port.getExternalPort(), + internalClientName, port.getInternalPort(), description); + + allPortMappings.add(newMapping); + } + + return allPortMappings; + } + + public String getCompleteDescription() { + final StringBuffer b = new StringBuffer(); + + b.append(" "); + b.append(remoteHost); + b.append(":"); + + b.append(" -> "); + b.append(internalClient); + b.append(":"); + + b.append(" "); + + b.append(" "); + b.append(description); + return b.toString(); + } + + public List getPorts() { + return ports; + } + + public void setPorts(final List ports) { + this.ports = ports; + } + + public void setDescription(final String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setRemoteHost(final String remoteHost) { + this.remoteHost = remoteHost; + } + + public String getRemoteHost() { + return remoteHost; + } + + public void setInternalClient(final String internalClient) { + this.internalClient = internalClient; + } + + public String getInternalClient() { + return internalClient; + } + + public boolean isNew() { + return isNew; + } + + public void setNew(final boolean isNew) { + this.isNew = isNew; + } + + public boolean useLocalhostAsInternalClient() { + return this.getInternalClient() == null || this.getInternalClient().length() == 0; + } + + public void save(final Settings settings) { + if (this.isNew) { + settings.addPreset(this); + } else { + settings.savePreset(this); + } + this.isNew = false; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/model/Protocol.java b/UPnP/src/main/java/org/chris/portmapper/model/Protocol.java new file mode 100644 index 0000000..028b9d2 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/model/Protocol.java @@ -0,0 +1,50 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.model; + +/** + * This {@link Enum} represents the protocol of a {@link SinglePortMapping}, possible values are {@link #TCP} and + * {@link #UDP}. + */ +public enum Protocol { + + TCP("TCP"), UDP("UDP"); + + private final String name; + + private Protocol(final String name) { + this.name = name; + } + + public static Protocol getProtocol(final String name) { + if (name != null && name.equalsIgnoreCase("TCP")) { + return TCP; + } + if (name != null && name.equalsIgnoreCase("UDP")) { + return UDP; + } + throw new IllegalArgumentException("Invalid protocol name '" + name + "'"); + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/UPnP/src/main/java/org/chris/portmapper/model/SinglePortMapping.java b/UPnP/src/main/java/org/chris/portmapper/model/SinglePortMapping.java new file mode 100644 index 0000000..e47e5e9 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/model/SinglePortMapping.java @@ -0,0 +1,74 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.model; + +import java.io.Serializable; + +/** + * This class is used by {@link PortMapping} to store the information about a single port mapping, i.e. the protocol + * (TCP or UDP) and internal and extern port. + */ +public class SinglePortMapping implements Cloneable, Serializable { + + private static final long serialVersionUID = 7458514232916039775L; + private int externalPort; + private int internalPort; + private Protocol protocol; + + public SinglePortMapping() { + this(Protocol.TCP, 1, 1); + } + + public SinglePortMapping(final Protocol protocol, final int internalPort, final int externalPort) { + this.protocol = protocol; + this.internalPort = internalPort; + this.externalPort = externalPort; + } + + public int getExternalPort() { + return externalPort; + } + + public void setExternalPort(final int externalPort) { + this.externalPort = externalPort; + } + + public Protocol getProtocol() { + return protocol; + } + + public void setProtocol(final Protocol protocol) { + this.protocol = protocol; + } + + public int getInternalPort() { + return internalPort; + } + + public void setInternalPort(final int internalPort) { + this.internalPort = internalPort; + } + + @Override + public Object clone() { + return new SinglePortMapping(protocol, internalPort, externalPort); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouter.java new file mode 100644 index 0000000..440b6b1 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouter.java @@ -0,0 +1,120 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This is the abstract super class for all routers. + */ +public abstract class AbstractRouter implements IRouter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final String name; + + public AbstractRouter(final String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + /** + * Get the the ip of the local host. + */ + @Override + public String getLocalHostAddress() throws RouterException { + logger.debug("Get IP of localhost"); + + final InetAddress localHostIP = getLocalHostAddressFromSocket(); + + // We do not want an address like 127.0.0.1 + if (localHostIP.getHostAddress().startsWith("127.")) { + throw new RouterException("Only found an address that begins with '127.' when retrieving IP of localhost"); + } + + return localHostIP.getHostAddress(); + } + + /** + * Get the ip of the local host by connecting to the router and fetching the ip from the socket. This only works + * when we are connected to the router and know its internal upnp port. + * + * @return the ip of the local host. + * @throws RouterException + */ + private InetAddress getLocalHostAddressFromSocket() throws RouterException { + InetAddress localHostIP = null; + try { + + // In order to use the socket method to get the address, we have to + // be connected to the router. + final int routerInternalPort = getInternalPort(); + logger.debug("Got internal router port {}", routerInternalPort); + + // Check, if we got a correct port number + if (routerInternalPort > 0) { + logger.debug("Creating socket to router: {}:{}...", getInternalHostName(), routerInternalPort); + try (Socket socket = new Socket(getInternalHostName(), routerInternalPort)) { + localHostIP = socket.getLocalAddress(); + } catch (final UnknownHostException e) { + throw new RouterException( + "Could not create socked to " + getInternalHostName() + ":" + routerInternalPort, e); + } + + logger.debug("Got address {} from socket.", localHostIP); + } else { + logger.debug("Got invalid internal router port number {}", routerInternalPort); + } + + // We are not connected to the router or got an invalid port number, + // so we have to use the traditional method. + if (localHostIP == null) { + + logger.debug( + "Not connected to router or got invalid port number, can not use socket to determine the address of the localhost. " + + "If no address is found, please connect to the router."); + + localHostIP = InetAddress.getLocalHost(); + + logger.debug("Got address {} via InetAddress.getLocalHost().", localHostIP); + } + + } catch (final IOException e) { + throw new RouterException("Could not get IP of localhost.", e); + } + return localHostIP; + } + + @Override + public String toString() { + return getName() + " (" + getInternalHostName() + ")"; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouterFactory.java b/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouterFactory.java new file mode 100644 index 0000000..7459df9 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/AbstractRouterFactory.java @@ -0,0 +1,91 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router; + +import static java.util.Arrays.*; + +import java.util.Collection; +import java.util.List; + +import org.chris.portmapper.PortMapperApp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The base class for all router factories. + */ +public abstract class AbstractRouterFactory { + + private static final String LOCATION_URL_SYSTEM_PROPERTY = "portmapper.locationUrl"; + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + protected final PortMapperApp app; + + private final String name; + + protected AbstractRouterFactory(final PortMapperApp app, final String name) { + this.app = app; + this.name = name; + } + + /** + * Get the name of the router factory that can be displayed to the user. + * + * @return the name of the router factory that can be displayed to the user. + */ + public String getName() { + return name; + } + + public List findRouters() throws RouterException { + final String locationUrl = System.getProperty(LOCATION_URL_SYSTEM_PROPERTY); + if (locationUrl == null) { + logger.debug("System property '{}' not defined: discover routers automatically.", + LOCATION_URL_SYSTEM_PROPERTY); + return findRoutersInternal(); + } + logger.info("Trying to connect using location url {}", locationUrl); + return asList(connect(locationUrl)); + } + + /** + * Search for routers on the network. + * + * @return the found router or an empty {@link Collection} if no router was found. + * @throws RouterException + * if something goes wrong during discovery. + */ + protected abstract List findRoutersInternal() throws RouterException; + + /** + * Directly connect to a router using a location url like http://192.168.179.1:49000/igddesc.xml. + * + * @param locationUrl + * a location url + * @return a router if the connection was successful. + * @throws RouterException + * if something goes wrong during connection. + */ + protected abstract IRouter connect(final String locationUrl) throws RouterException; + + @Override + public String toString() { + return name; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/IRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/IRouter.java new file mode 100644 index 0000000..e19242b --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/IRouter.java @@ -0,0 +1,121 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router; + +import java.util.Collection; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; + +public interface IRouter { + + public abstract String getName(); + + /** + * Get the IP address of the local host. + * + * @return IP address of the local host or null, if the address could not be determined. + * @throws RouterException + */ + public String getLocalHostAddress() throws RouterException; + + /** + * Get the external IP of the router. + * + * @return the external IP of the router. + */ + public abstract String getExternalIPAddress() throws RouterException; + + /** + * Get the internal host name or IP of the router. + * + * @return the internal host name or IP of the router. + * @throws RouterException + */ + public abstract String getInternalHostName(); + + /** + * Get the internal port of the router. + * + * @return the internal port of the router. + * @throws RouterException + */ + public abstract int getInternalPort() throws RouterException; + + /** + * Get all port mappings from the router. + * + * @return all port mappings from the router. + * @throws RouterException + * if something went wrong when getting the port mappings. + */ + public abstract Collection getPortMappings() throws RouterException; + + /** + * Write information about the router to the log. + * + * @throws RouterException + */ + public abstract void logRouterInfo() throws RouterException; + + /** + * Add the given port mappings to the router. + * + * @param mappings + * the port mappings to add. + * @throws RouterException + */ + public abstract void addPortMappings(Collection mappings) throws RouterException; + + /** + * Add the given port mapping to the router. + * + * @param mapping + * the port mapping to add. + * @throws RouterException + */ + public abstract void addPortMapping(PortMapping mapping) throws RouterException; + + /** + * Remove the given port mapping from the router. + * + * @param mapping + * the port mapping to remove. + * @throws RouterException + */ + public abstract void removeMapping(PortMapping mapping) throws RouterException; + + /** + * Remove the port mapping with the given data from the router. + * + * @param protocol + * @param remoteHost + * @param externalPort + * @throws RouterException + */ + public abstract void removePortMapping(Protocol protocol, String remoteHost, int externalPort) + throws RouterException; + + /** + * Disconnect from the router. + */ + public abstract void disconnect(); +} \ No newline at end of file diff --git a/UPnP/src/main/java/org/chris/portmapper/router/RouterException.java b/UPnP/src/main/java/org/chris/portmapper/router/RouterException.java new file mode 100644 index 0000000..6f27efd --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/RouterException.java @@ -0,0 +1,31 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router; + +public class RouterException extends Exception { + + private static final long serialVersionUID = 1L; + + public RouterException(final String message, final Throwable cause) { + super(message, cause); + } + + public RouterException(final String message) { + super(message); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingOperationFailedException.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingOperationFailedException.java new file mode 100644 index 0000000..c44259c --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingOperationFailedException.java @@ -0,0 +1,36 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling; + +import org.fourthline.cling.model.message.control.IncomingActionResponseMessage; + +public class ClingOperationFailedException extends ClingRouterException { + + private static final long serialVersionUID = 1L; + private final IncomingActionResponseMessage response; + + public ClingOperationFailedException(final String message, final IncomingActionResponseMessage response) { + super(message); + assert response.getOperation().isFailed(); + this.response = response; + } + + public IncomingActionResponseMessage getResponse() { + return response; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingPortMappingExtractor.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingPortMappingExtractor.java new file mode 100644 index 0000000..63e9e88 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingPortMappingExtractor.java @@ -0,0 +1,154 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling; + +import java.util.Collection; +import java.util.LinkedList; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.router.RouterException; +import org.chris.portmapper.router.cling.action.ActionService; +import org.chris.portmapper.router.cling.action.GetPortMappingEntryAction; +import org.fourthline.cling.model.message.control.IncomingActionResponseMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sbbi.upnp.impls.InternetGatewayDevice; + +/** + * This class fetches all {@link PortMapping} from an {@link InternetGatewayDevice}. + */ +class ClingPortMappingExtractor { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final Collection mappings; + private boolean moreEntries; + private int currentMappingNumber; + + /** + * The maximum number of port mappings that we will try to retrieve from the router. + */ + private final int maxNumPortMappings; + private final ActionService actionService; + + ClingPortMappingExtractor(final ActionService actionService, final int maxNumPortMappings) { + this.actionService = actionService; + this.maxNumPortMappings = maxNumPortMappings; + this.mappings = new LinkedList<>(); + this.moreEntries = true; + this.currentMappingNumber = 0; + } + + public Collection getPortMappings() throws RouterException { + + /* + * This is a little trick to get all port mappings. There is a method that gets the number of available port + * mappings (getNatMappingsCount()), but it seems, that this method just tries to get all port mappings and + * checks, if an error is returned. + * + * In order to speed this up, we will do the same here, but stop, when the first exception is thrown. + */ + + while (morePortMappingsAvailable()) { + logger.debug("Getting port mapping with entry number " + currentMappingNumber + "..."); + + try { + final PortMapping portMapping = actionService + .run(new GetPortMappingEntryAction(actionService.getService(), currentMappingNumber)); + mappings.add(portMapping); + } catch (final ClingOperationFailedException e) { + handleFailureResponse(e.getResponse()); + } + currentMappingNumber++; + } + + checkMaxNumPortMappingsReached(); + + return mappings; + } + + /** + * Check, if the max number of entries is reached and print a warning message. + */ + private void checkMaxNumPortMappingsReached() { + if (currentMappingNumber == maxNumPortMappings) { + logger.warn( + "Reached max number of port mappings to get ({}). Perhaps not all port mappings where retrieved.", + maxNumPortMappings); + } + } + + private boolean morePortMappingsAvailable() { + return moreEntries && currentMappingNumber < maxNumPortMappings; + } + + private void handleFailureResponse(final IncomingActionResponseMessage incomingActionResponseMessage) { + if (isNoMoreMappingsException(incomingActionResponseMessage)) { + moreEntries = false; + logger.debug("Got no port mapping for entry number {} (status: {}). Stop getting more entries.", + currentMappingNumber, incomingActionResponseMessage.getOperation().getStatusMessage()); + } else { + moreEntries = false; + logger.info( + "Got error response when fetching port mapping for entry number {}: '{}'. Stop getting more entries.", + currentMappingNumber, incomingActionResponseMessage); + } + } + + /** + * This method checks, if the error code of the given exception means, that no more mappings are available. + *

+ * The following error codes are recognized: + *

    + *
  • SpecifiedArrayIndexInvalid: 713
  • + *
  • NoSuchEntryInArray: 714
  • + *
  • Invalid Args: 402 (e.g. for DD-WRT, TP-LINK TL-R460 firmware 4.7.6 Build 100714 Rel.63134n)
  • + *
  • Other errors, e.g. "The reference to entity "T" must end with the ';' delimiter" or + * "Content is not allowed in prolog": 899 (e.g. ActionTec MI424-WR, Thomson TWG850-4U)
  • + *
+ * See bug reports + * + * + * @param incomingActionResponseMessage + * the exception to check + * @return true, if the given exception means, that no more port mappings are available, else + * false. + */ + private boolean isNoMoreMappingsException(final IncomingActionResponseMessage incomingActionResponseMessage) { + final int errorCode = incomingActionResponseMessage.getOperation().getStatusCode(); + switch (errorCode) { + case 713: + case 714: + case 402: + case 899: + return true; + + default: + return false; + } + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRegistryListener.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRegistryListener.java new file mode 100644 index 0000000..d1f0f4e --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRegistryListener.java @@ -0,0 +1,110 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling; + +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; + +import org.fourthline.cling.model.meta.Device; +import org.fourthline.cling.model.meta.Service; +import org.fourthline.cling.model.types.DeviceType; +import org.fourthline.cling.model.types.ServiceType; +import org.fourthline.cling.model.types.UDADeviceType; +import org.fourthline.cling.model.types.UDAServiceType; +import org.fourthline.cling.registry.DefaultRegistryListener; +import org.fourthline.cling.registry.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClingRegistryListener extends DefaultRegistryListener { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + public static final DeviceType IGD_DEVICE_TYPE = new UDADeviceType("InternetGatewayDevice", 1); + public static final DeviceType CONNECTION_DEVICE_TYPE = new UDADeviceType("WANConnectionDevice", 1); + + public static final ServiceType IP_SERVICE_TYPE = new UDAServiceType("WANIPConnection", 1); + public static final ServiceType PPP_SERVICE_TYPE = new UDAServiceType("WANPPPConnection", 1); + + private final SynchronousQueue> foundServices; + + public ClingRegistryListener() { + this.foundServices = new SynchronousQueue<>(); + } + + public Service waitForServiceFound(final long timeout, final TimeUnit unit) { + try { + return foundServices.poll(timeout, unit); + } catch (final InterruptedException e) { + logger.warn("Interrupted when waiting for a service"); + return null; + } + } + + @Override + public void deviceAdded(final Registry registry, @SuppressWarnings("rawtypes") final Device device) { + @SuppressWarnings("unchecked") + final Service connectionService = discoverConnectionService(device); + if (connectionService == null) { + return; + } + + logger.debug("Found connection service {}", connectionService); + foundServices.offer(connectionService); + } + + protected Service discoverConnectionService(@SuppressWarnings("rawtypes") final Device device) { + if (!device.getType().equals(IGD_DEVICE_TYPE)) { + logger.debug("Found service of wrong type {}, expected {}.", device.getType(), IGD_DEVICE_TYPE); + return null; + } + + @SuppressWarnings("rawtypes") + final Device[] connectionDevices = device.findDevices(CONNECTION_DEVICE_TYPE); + if (connectionDevices.length == 0) { + logger.debug("IGD doesn't support '{}': {}", CONNECTION_DEVICE_TYPE, device); + return null; + } + + logger.debug("Found {} devices", connectionDevices.length); + + return findConnectionService(connectionDevices); + } + + @SuppressWarnings("rawtypes") + private Service findConnectionService(final Device[] connectionDevices) { + for (final Device connectionDevice : connectionDevices) { + + final Service ipConnectionService = connectionDevice.findService(IP_SERVICE_TYPE); + final Service pppConnectionService = connectionDevice.findService(PPP_SERVICE_TYPE); + + if (ipConnectionService != null) { + logger.debug("Device {} supports ip service type: {}", connectionDevice, ipConnectionService); + return ipConnectionService; + } + if (pppConnectionService != null) { + logger.debug("Device {} supports ppp service type: {}", connectionDevice, pppConnectionService); + return pppConnectionService; + } + + logger.debug("IGD {} doesn't support IP or PPP WAN connection service", connectionDevice); + } + logger.debug("None of the {} devices supports IP or PPP WAN connections", connectionDevices.length); + return null; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouter.java new file mode 100644 index 0000000..3f66ba9 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouter.java @@ -0,0 +1,145 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling; + +import java.net.URI; +import java.util.Collection; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.chris.portmapper.router.AbstractRouter; +import org.chris.portmapper.router.RouterException; +import org.chris.portmapper.router.cling.action.ActionService; +import org.chris.portmapper.router.cling.action.AddPortMappingAction; +import org.chris.portmapper.router.cling.action.DeletePortMappingAction; +import org.chris.portmapper.router.cling.action.GetExternalIpAction; +import org.fourthline.cling.controlpoint.ControlPoint; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.model.meta.Service; +import org.fourthline.cling.model.meta.UDAVersion; +import org.fourthline.cling.registry.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClingRouter extends AbstractRouter { + + /** + * The maximum number of port mappings that we will try to retrieve from the router. + */ + private final static int MAX_NUM_PORTMAPPINGS = 500; + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final RemoteService service; + + private final Registry registry; + + private final ActionService actionService; + + public ClingRouter(final RemoteService service, final Registry registry, final ControlPoint controlPoint) { + super(getName(service)); + this.service = service; + this.registry = registry; + actionService = new ActionService(service, controlPoint); + } + + private static String getName(final Service service) { + return service.getDevice().getDisplayString(); + } + + @Override + public String getExternalIPAddress() throws RouterException { + return actionService.run(new GetExternalIpAction(service)); + } + + @Override + public String getInternalHostName() { + final URI uri = getUri(); + return uri != null ? uri.getHost() : null; + } + + @Override + public int getInternalPort() throws RouterException { + final URI uri = getUri(); + return uri != null ? uri.getPort() : null; + } + + private URI getUri() { + if (service.getDevice().getDetails().getPresentationURI() != null) { + return service.getDevice().getDetails().getPresentationURI(); + } + if (service.getControlURI() != null) { + return service.getControlURI(); + } + if (service.getDescriptorURI() != null) { + return service.getDescriptorURI(); + } + if (service.getEventSubscriptionURI() != null) { + return service.getEventSubscriptionURI(); + } + return null; + } + + @Override + public Collection getPortMappings() throws RouterException { + return new ClingPortMappingExtractor(actionService, MAX_NUM_PORTMAPPINGS).getPortMappings(); + } + + @Override + public void logRouterInfo() throws RouterException { + logger.info("Service id: " + service.getServiceId()); + logger.info("Reference: " + service.getReference()); + logger.info("Display name: " + service.getDevice().getDisplayString()); + final UDAVersion version = service.getDevice().getVersion(); + logger.info("Version: " + version.getMajor() + "." + version.getMinor()); + logger.info("Control uri: {}", service.getControlURI()); + logger.info("Descriptor uri: {}", service.getDescriptorURI()); + logger.info("Event subscription uri: {}", service.getEventSubscriptionURI()); + logger.info("Device base url: {}", service.getDevice().getDetails().getBaseURL()); + logger.info("Device presentation uri: {}", service.getDevice().getDetails().getPresentationURI()); + } + + @Override + public void addPortMappings(final Collection mappings) throws RouterException { + for (final PortMapping portMapping : mappings) { + addPortMapping(portMapping); + } + } + + @Override + public void addPortMapping(final PortMapping mapping) throws RouterException { + actionService.run(new AddPortMappingAction(service, mapping)); + } + + @Override + public void removeMapping(final PortMapping mapping) throws RouterException { + actionService.run(new DeletePortMappingAction(service, mapping)); + } + + @Override + public void removePortMapping(final Protocol protocol, final String remoteHost, final int externalPort) + throws RouterException { + removeMapping(new PortMapping(protocol, remoteHost, externalPort, null, 0, null)); + } + + @Override + public void disconnect() { + logger.debug("Shutdown registry"); + registry.shutdown(); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterException.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterException.java new file mode 100644 index 0000000..45039fc --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterException.java @@ -0,0 +1,34 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.cling; + +public class ClingRouterException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public ClingRouterException(final String message, final Throwable cause) { + super(message, cause); + } + + public ClingRouterException(final String message) { + super(message); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterFactory.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterFactory.java new file mode 100644 index 0000000..d0662d3 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/ClingRouterFactory.java @@ -0,0 +1,89 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling; + +import java.util.Arrays; +import java.util.Collections; +import java.util.EventObject; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.chris.portmapper.PortMapperApp; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; +import org.fourthline.cling.DefaultUpnpServiceConfiguration; +import org.fourthline.cling.UpnpService; +import org.fourthline.cling.UpnpServiceConfiguration; +import org.fourthline.cling.UpnpServiceImpl; +import org.fourthline.cling.model.meta.RemoteService; +import org.jdesktop.application.Application.ExitListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClingRouterFactory extends AbstractRouterFactory { + + private static final long DISCOVERY_TIMEOUT_SECONDS = 5; + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + public ClingRouterFactory(final PortMapperApp app) { + super(app, "Cling lib"); + } + + @Override + protected List findRoutersInternal() throws RouterException { + final UpnpServiceConfiguration config = new DefaultUpnpServiceConfiguration(); + final ClingRegistryListener clingRegistryListener = new ClingRegistryListener(); + final UpnpService upnpService = new UpnpServiceImpl(config, clingRegistryListener); + shutdownServiceOnExit(upnpService); + + log.debug("Start searching using upnp service"); + upnpService.getControlPoint().search(); + final RemoteService service = (RemoteService) clingRegistryListener + .waitForServiceFound(DISCOVERY_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + if (service == null) { + log.debug("Did not find a service after {} seconds", DISCOVERY_TIMEOUT_SECONDS); + return Collections.emptyList(); + } + + log.debug("Found service {}", service); + return Arrays + . asList(new ClingRouter(service, upnpService.getRegistry(), upnpService.getControlPoint())); + } + + private void shutdownServiceOnExit(final UpnpService upnpService) { + app.addExitListener(new ExitListener() { + @Override + public void willExit(final EventObject event) { + log.debug("Shutdown upnp service"); + upnpService.shutdown(); + } + + @Override + public boolean canExit(final EventObject event) { + return true; + } + }); + } + + @Override + protected IRouter connect(final String locationUrl) throws RouterException { + throw new UnsupportedOperationException("Direct connection is not supported for Cling library."); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AbstractClingAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AbstractClingAction.java new file mode 100644 index 0000000..ac80c13 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AbstractClingAction.java @@ -0,0 +1,83 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.chris.portmapper.router.cling.ClingRouterException; +import org.fourthline.cling.model.action.ActionArgumentValue; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.Action; +import org.fourthline.cling.model.meta.ActionArgument; +import org.fourthline.cling.model.meta.ActionArgument.Direction; +import org.fourthline.cling.model.meta.RemoteDevice; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.model.meta.Service; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +abstract class AbstractClingAction implements ClingAction { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final Service service; + private final String actionName; + + public AbstractClingAction(final Service service, final String actionName) { + this.service = service; + this.actionName = actionName; + } + + public Map getArgumentValues() { + return Collections.emptyMap(); + } + + @SuppressWarnings("unchecked") + @Override + public ActionInvocation getActionInvocation() { + final Action action = service.getAction(actionName); + if (action == null) { + throw new ClingRouterException("No action found for name '" + actionName + "'. Available actions: " + + Arrays.toString(service.getActions())); + } + @SuppressWarnings("rawtypes") + final ActionArgumentValue[] argumentArray = getArguments(action); + return new ActionInvocation(action, argumentArray); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private ActionArgumentValue[] getArguments(final Action action) { + final ActionArgument[] actionArguments = action.getArguments(); + final Map argumentValues = getArgumentValues(); + final List> actionArgumentValues = new ArrayList<>(actionArguments.length); + + for (final ActionArgument actionArgument : actionArguments) { + if (actionArgument.getDirection() == Direction.IN) { + final Object value = argumentValues.get(actionArgument.getName()); + logger.trace("Action {}: add arg value for {}: {} (expected datatype: {})", action.getName(), + actionArgument, value, actionArgument.getDatatype().getDisplayString()); + actionArgumentValues.add(new ActionArgumentValue<>(actionArgument, value)); + } + } + return actionArgumentValues.toArray(new ActionArgumentValue[actionArgumentValues.size()]); + } +} \ No newline at end of file diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ActionService.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ActionService.java new file mode 100644 index 0000000..3a77bbb --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ActionService.java @@ -0,0 +1,60 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import java.net.URL; + +import org.chris.portmapper.router.cling.ClingOperationFailedException; +import org.chris.portmapper.router.cling.ClingRouterException; +import org.fourthline.cling.controlpoint.ControlPoint; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.message.control.IncomingActionResponseMessage; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.protocol.sync.SendingAction; + +public class ActionService { + private final RemoteService remoteService; + private final ControlPoint controlPoint; + + public ActionService(final RemoteService remoteService, final ControlPoint controlPoint) { + this.remoteService = remoteService; + this.controlPoint = controlPoint; + } + + public T run(final ClingAction action) { + // Figure out the remote URL where we'd like to send the action request to + final URL controLURL = remoteService.getDevice().normalizeURI(remoteService.getControlURI()); + + final ActionInvocation actionInvocation = action.getActionInvocation(); + final SendingAction prot = controlPoint.getProtocolFactory().createSendingAction(actionInvocation, controLURL); + prot.run(); + + final IncomingActionResponseMessage response = prot.getOutputMessage(); + if (response == null) { + throw new ClingRouterException("Got null response for action " + actionInvocation); + } else if (response.getOperation().isFailed()) { + throw new ClingOperationFailedException("Invocation " + actionInvocation + " failed with operation '" + + response.getOperation() + "', body '" + response.getBodyString() + "'", response); + } + return action.convert(actionInvocation); + } + + public RemoteService getService() { + return remoteService; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AddPortMappingAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AddPortMappingAction.java new file mode 100644 index 0000000..cb8fa84 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/AddPortMappingAction.java @@ -0,0 +1,58 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import java.util.HashMap; +import java.util.Map; + +import org.chris.portmapper.model.PortMapping; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.RemoteDevice; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.model.meta.Service; +import org.fourthline.cling.model.types.UnsignedIntegerFourBytes; +import org.fourthline.cling.model.types.UnsignedIntegerTwoBytes; + +public class AddPortMappingAction extends AbstractClingAction { + + private final PortMapping portMapping; + + public AddPortMappingAction(final Service service, final PortMapping portMapping) { + super(service, "AddPortMapping"); + this.portMapping = portMapping; + } + + @Override + public Map getArgumentValues() { + final HashMap args = new HashMap<>(); + args.put("NewExternalPort", new UnsignedIntegerTwoBytes(portMapping.getExternalPort())); + args.put("NewProtocol", portMapping.getProtocol()); + args.put("NewInternalClient", portMapping.getInternalClient()); + args.put("NewInternalPort", new UnsignedIntegerTwoBytes(portMapping.getInternalPort())); + args.put("NewLeaseDuration", new UnsignedIntegerFourBytes(portMapping.getLeaseDuration())); + args.put("NewEnabled", portMapping.isEnabled()); + args.put("NewRemoteHost", portMapping.getRemoteHost()); + args.put("NewPortMappingDescription", portMapping.getDescription()); + return args; + } + + @Override + public Void convert(final ActionInvocation response) { + return null; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ClingAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ClingAction.java new file mode 100644 index 0000000..e4c9b01 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/ClingAction.java @@ -0,0 +1,28 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.RemoteService; + +public interface ClingAction { + + ActionInvocation getActionInvocation(); + + T convert(ActionInvocation response); +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/DeletePortMappingAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/DeletePortMappingAction.java new file mode 100644 index 0000000..1df427e --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/DeletePortMappingAction.java @@ -0,0 +1,54 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import java.util.HashMap; +import java.util.Map; + +import org.chris.portmapper.model.PortMapping; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.model.types.UnsignedIntegerTwoBytes; + +public class DeletePortMappingAction extends AbstractClingAction { + + private final int externalPort; + private final String protocol; + private final String remoteHost; + + public DeletePortMappingAction(final RemoteService service, final PortMapping portMapping) { + super(service, "DeletePortMapping"); + this.externalPort = portMapping.getExternalPort(); + this.protocol = portMapping.getProtocol().getName(); + this.remoteHost = portMapping.getRemoteHost(); + } + + @Override + public Map getArgumentValues() { + final HashMap args = new HashMap<>(); + args.put("NewExternalPort", new UnsignedIntegerTwoBytes(externalPort)); + args.put("NewProtocol", protocol); + args.put("NewRemoteHost", remoteHost); + return args; + } + + @Override + public Void convert(final ActionInvocation response) { + return null; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetExternalIpAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetExternalIpAction.java new file mode 100644 index 0000000..39ad304 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetExternalIpAction.java @@ -0,0 +1,36 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.cling.action; + +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.RemoteService; + +public class GetExternalIpAction extends AbstractClingAction { + + public GetExternalIpAction(final RemoteService service) { + super(service, "GetExternalIPAddress"); + } + + @Override + public String convert(final ActionInvocation invocation) { + return (String) invocation.getOutput("NewExternalIPAddress").getValue(); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetPortMappingEntryAction.java b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetPortMappingEntryAction.java new file mode 100644 index 0000000..ce54368 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/cling/action/GetPortMappingEntryAction.java @@ -0,0 +1,75 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.cling.action; + +import java.util.Collections; +import java.util.Map; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.fourthline.cling.model.action.ActionInvocation; +import org.fourthline.cling.model.meta.RemoteDevice; +import org.fourthline.cling.model.meta.RemoteService; +import org.fourthline.cling.model.meta.Service; +import org.fourthline.cling.model.types.UnsignedIntegerFourBytes; +import org.fourthline.cling.model.types.UnsignedIntegerTwoBytes; + +public class GetPortMappingEntryAction extends AbstractClingAction { + + private final int index; + + public GetPortMappingEntryAction(final Service service, final int index) { + super(service, "GetGenericPortMappingEntry"); + this.index = index; + } + + @Override + public Map getArgumentValues() { + return Collections. singletonMap("NewPortMappingIndex", new UnsignedIntegerTwoBytes(index)); + } + + @Override + public PortMapping convert(final ActionInvocation response) { + final Protocol protocol = Protocol.getProtocol(getStringValue(response, "NewProtocol")); + final String remoteHost = getStringValue(response, "NewRemoteHost"); + final int externalPort = getIntValue(response, "NewExternalPort"); + final String internalClient = getStringValue(response, "NewInternalClient"); + final int internalPort = getIntValue(response, "NewInternalPort"); + final String description = getStringValue(response, "NewPortMappingDescription"); + final boolean enabled = getBooleanValue(response, "NewEnabled"); + final long leaseDuration = getLongValue(response, "NewLeaseDuration"); + return new PortMapping(protocol, remoteHost, externalPort, internalClient, internalPort, description, enabled, + leaseDuration); + } + + private boolean getBooleanValue(final ActionInvocation response, final String argumentName) { + return (boolean) response.getOutput(argumentName).getValue(); + } + + protected int getIntValue(final ActionInvocation response, final String argumentName) { + return ((UnsignedIntegerTwoBytes) response.getOutput(argumentName).getValue()).getValue().intValue(); + } + + protected long getLongValue(final ActionInvocation response, final String argumentName) { + return ((UnsignedIntegerFourBytes) response.getOutput(argumentName).getValue()).getValue().longValue(); + } + + protected String getStringValue(final ActionInvocation response, final String argumentName) { + return (String) response.getOutput(argumentName).getValue(); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouter.java new file mode 100644 index 0000000..bd2bea6 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouter.java @@ -0,0 +1,110 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.dummy; + +import java.util.Collection; +import java.util.LinkedList; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.chris.portmapper.router.AbstractRouter; +import org.chris.portmapper.router.RouterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DummyRouter extends AbstractRouter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final Collection mappings; + + public DummyRouter(final String name) { + super(name); + logger.debug("Created new DummyRouter"); + mappings = new LinkedList<>(); + mappings.add(new PortMapping(Protocol.TCP, "remoteHost1", 1, "internalClient1", 1, + getName() + ": dummy port mapping 1")); + mappings.add( + new PortMapping(Protocol.UDP, null, 2, "internalClient2", 2, getName() + ": dummy port mapping 2")); + mappings.add( + new PortMapping(Protocol.TCP, null, 3, "internalClient3", 3, getName() + ": dummy port mapping 3")); + } + + @Override + public void addPortMapping(final PortMapping mapping) { + logger.debug("Adding mapping " + mapping); + mappings.add(mapping); + } + + @Override + public void addPortMappings(final Collection mappingsToAdd) { + logger.debug("Adding {} mappings: {}", mappingsToAdd.size(), mappingsToAdd); + this.mappings.addAll(mappingsToAdd); + } + + @Override + public void disconnect() { + logger.debug("Disconnect"); + } + + @Override + public String getExternalIPAddress() { + return "DummyExternalIP"; + } + + @Override + public String getInternalHostName() { + return "DummyInternalHostName"; + } + + @Override + public int getInternalPort() { + return 42; + } + + @Override + public Collection getPortMappings() { + try { + logger.debug("Sleep 3s to simulate delay when fetching port mappings."); + Thread.sleep(3000); + } catch (final InterruptedException e) { + // ignore + } + return mappings; + } + + @Override + public void logRouterInfo() { + logger.info("DummyRouter " + getName()); + } + + @Override + public void removeMapping(final PortMapping mapping) { + mappings.remove(mapping); + } + + @Override + public void removePortMapping(final Protocol protocol, final String remoteHost, final int externalPort) { + // ignore + } + + @Override + public String getLocalHostAddress() throws RouterException { + return "DummyLocalhostAddress"; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouterFactory.java b/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouterFactory.java new file mode 100644 index 0000000..45598d8 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/dummy/DummyRouterFactory.java @@ -0,0 +1,52 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.dummy; + +import java.util.LinkedList; +import java.util.List; + +import org.chris.portmapper.PortMapperApp; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; + +/** + * Router factory for testing without a real router. + */ +public class DummyRouterFactory extends AbstractRouterFactory { + + public DummyRouterFactory(final PortMapperApp app) { + super(app, "Dummy library"); + } + + @Override + protected List findRoutersInternal() throws RouterException { + final List routers = new LinkedList<>(); + routers.add(new DummyRouter("DummyRouter1")); + routers.add(new DummyRouter("DummyRouter2")); + return routers; + } + + @Override + protected IRouter connect(final String locationUrl) throws RouterException { + return new DummyRouter("DummyRouter @ " + locationUrl); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIPortMappingExtractor.java b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIPortMappingExtractor.java new file mode 100644 index 0000000..7a07a68 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIPortMappingExtractor.java @@ -0,0 +1,182 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.sbbi; + +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedList; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.router.RouterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sbbi.upnp.impls.InternetGatewayDevice; +import net.sbbi.upnp.messages.ActionResponse; +import net.sbbi.upnp.messages.UPNPResponseException; + +/** + * This class fetches all {@link PortMapping} from an {@link InternetGatewayDevice}. + */ +class SBBIPortMappingExtractor { + + private final Logger logger; + private final InternetGatewayDevice router; + private final Collection mappings; + private boolean moreEntries; + private int currentMappingNumber; + private int nullPortMappings; + + /** + * The maximum number of port mappings that we will try to retrieve from the router. + */ + private final int maxNumPortMappings; + + SBBIPortMappingExtractor(final InternetGatewayDevice router, final int maxNumPortMappings) { + this(router, maxNumPortMappings, LoggerFactory.getLogger(SBBIPortMappingExtractor.class)); + } + + SBBIPortMappingExtractor(final InternetGatewayDevice router, final int maxNumPortMappings, final Logger logger) { + this.router = router; + this.maxNumPortMappings = maxNumPortMappings; + this.logger = logger; + this.mappings = new LinkedList<>(); + this.moreEntries = true; + this.currentMappingNumber = 0; + this.nullPortMappings = 0; + } + + public Collection getPortMappings() throws RouterException { + + try { + + /* + * This is a little trick to get all port mappings. There is a method that gets the number of available port + * mappings (getNatMappingsCount()), but it seems, that this method just tries to get all port mappings and + * checks, if an error is returned. + * + * In order to speed this up, we will do the same here, but stop, when the first exception is thrown. + */ + + while (morePortMappingsAvailable()) { + logger.debug("Getting port mapping with entry number {}...", currentMappingNumber); + + try { + final ActionResponse response = router.getGenericPortMappingEntry(currentMappingNumber); + addResponse(response); + } catch (final UPNPResponseException e) { + handleUPNPResponseException(e); + } + + currentMappingNumber++; + } + + checkMaxNumPortMappingsReached(); + + } catch (final IOException e) { + throw new RouterException("Could not get NAT mappings: " + e.getMessage(), e); + } + + logger.debug("Found {} mappings, {} mappings returned as null.", mappings.size(), nullPortMappings); + return mappings; + } + + /** + * Check, if the max number of entries is reached and print a warning message. + */ + private void checkMaxNumPortMappingsReached() { + if (currentMappingNumber == maxNumPortMappings) { + logger.warn( + "Reached max number of port mappings to get ({}). Perhaps not all port mappings where retrieved.", + maxNumPortMappings); + } + } + + private boolean morePortMappingsAvailable() { + return moreEntries && currentMappingNumber < maxNumPortMappings; + } + + private void addResponse(final ActionResponse response) { + // Create a port mapping for the response. + if (response != null) { + final PortMapping newMapping = PortMapping.create(response); + if (logger.isTraceEnabled()) { + logger.trace("Got port mapping #{}: {}", currentMappingNumber, newMapping.getCompleteDescription()); + } + mappings.add(newMapping); + } else { + nullPortMappings++; + logger.trace("Got a null port mapping for number {} ({} so far)", currentMappingNumber, nullPortMappings); + } + } + + private void handleUPNPResponseException(final UPNPResponseException e) { + if (isNoMoreMappingsException(e)) { + moreEntries = false; + logger.debug( + "Got no port mapping for entry number {} (error code: {}, error description: {}). Stop getting more entries.", + currentMappingNumber, e.getDetailErrorCode(), e.getDetailErrorDescription()); + } else { + moreEntries = false; + logger.error("Got exception when fetching port mapping for entry number " + currentMappingNumber + + ". Stop getting more entries.", e); + } + } + + /** + * This method checks, if the error code of the given exception means, that no more mappings are available. + *

+ * The following error codes are recognized: + *

    + *
  • SpecifiedArrayIndexInvalid: 713
  • + *
  • NoSuchEntryInArray: 714
  • + *
  • Invalid Args: 402 (e.g. for DD-WRT, TP-LINK TL-R460 firmware 4.7.6 Build 100714 Rel.63134n)
  • + *
  • Other errors, e.g. "The reference to entity "T" must end with the ';' delimiter" or + * "Content is not allowed in prolog": 899 (e.g. ActionTec MI424-WR, Thomson TWG850-4U)
  • + *
+ * See bug reports + * + * + * @param e + * the exception to check + * @return true, if the given exception means, that no more port mappings are available, else + * false. + */ + private boolean isNoMoreMappingsException(final UPNPResponseException e) { + final int errorCode = e.getDetailErrorCode(); + switch (errorCode) { + case 713: + case 714: + case 402: + case 899: + return true; + + default: + return false; + } + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouter.java new file mode 100644 index 0000000..2fa279d --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouter.java @@ -0,0 +1,233 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.sbbi; + +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.chris.portmapper.PortMapperApp; +import org.chris.portmapper.Settings; +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.chris.portmapper.router.AbstractRouter; +import org.chris.portmapper.router.RouterException; +import org.chris.portmapper.util.EncodingUtilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.sbbi.upnp.devices.UPNPRootDevice; +import net.sbbi.upnp.impls.InternetGatewayDevice; +import net.sbbi.upnp.messages.UPNPResponseException; + +/** + * This class represents a router device and provides methods for managing port mappings and getting information about + * the router. It useses the SBBI library's {@link InternetGatewayDevice}. + */ +public class SBBIRouter extends AbstractRouter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * The wrapped router device. + */ + final private InternetGatewayDevice router; + + /** + * The maximum number of port mappings that we will try to retrieve from the router. + */ + private final static int MAX_NUM_PORTMAPPINGS = 500; + + private final PortMapperApp app; + + SBBIRouter(final PortMapperApp app, final InternetGatewayDevice router) { + super(router.getIGDRootDevice().getModelName()); + this.app = app; + this.router = router; + } + + @Override + public String getExternalIPAddress() throws RouterException { + logger.debug("Get external IP address..."); + String ipAddress; + try { + ipAddress = router.getExternalIPAddress(); + } catch (final UPNPResponseException e) { + throw new RouterException("Could not get external IP", e); + } catch (final IOException e) { + throw new RouterException("Could not get external IP", e); + } + logger.info("Got external IP address " + ipAddress + " for router."); + return ipAddress; + } + + @Override + public String getInternalHostName() { + logger.debug("Get internal IP address..."); + final URL presentationURL = router.getIGDRootDevice().getPresentationURL(); + if (presentationURL == null) { + logger.warn("Did not get presentation url"); + return null; + } + final String ipAddress = presentationURL.getHost(); + logger.info("Got internal host name '{}' for router.", ipAddress); + return ipAddress; + } + + @Override + public int getInternalPort() { + logger.debug("Get internal port of router..."); + final URL presentationURL = router.getIGDRootDevice().getPresentationURL(); + // Presentation URL may be null in some situations. + if (presentationURL != null) { + final int presentationUrlPort = presentationURL.getPort(); + // https://sourceforge.net/tracker/?func=detail&aid=3198378&group_id=213879&atid=1027466 + // Some routers send an invalid presentationURL, in this case use + // URLBase. + if (presentationUrlPort > 0) { + logger.debug("Got valid internal port {} from presentation URL '{}'", presentationUrlPort, + presentationURL); + return presentationUrlPort; + } else { + logger.debug("Got invalid port {} from presentation url '{}'", presentationUrlPort, presentationURL); + } + } else { + logger.debug("Presentation url is null"); + } + final URL urlBase = router.getIGDRootDevice().getURLBase(); + final int urlBasePort = urlBase.getPort(); + logger.debug("Presentation URL is null or returns invalid port: using port {} of base url '{}'", urlBasePort, + urlBase); + + return urlBasePort; + } + + @Override + public Collection getPortMappings() throws RouterException { + return new SBBIPortMappingExtractor(router, MAX_NUM_PORTMAPPINGS).getPortMappings(); + } + + @Override + public void logRouterInfo() throws RouterException { + final Map info = new HashMap<>(); + final UPNPRootDevice rootDevice = router.getIGDRootDevice(); + info.put("friendlyName", rootDevice.getFriendlyName()); + info.put("manufacturer", rootDevice.getManufacturer()); + info.put("modelDescription", rootDevice.getModelDescription()); + info.put("modelName", rootDevice.getModelName()); + info.put("serialNumber", rootDevice.getSerialNumber()); + info.put("vendorFirmware", rootDevice.getVendorFirmware()); + + info.put("modelNumber", rootDevice.getModelNumber()); + info.put("modelURL", rootDevice.getModelURL()); + info.put("manufacturerURL", rootDevice.getManufacturerURL().toExternalForm()); + info.put("presentationURL", + rootDevice.getPresentationURL() != null ? rootDevice.getPresentationURL().toExternalForm() : null); + info.put("urlBase", rootDevice.getURLBase().toExternalForm()); + + final SortedSet sortedKeys = new TreeSet<>(info.keySet()); + + for (final String key : sortedKeys) { + final String value = info.get(key); + logger.info("Router Info: {} \t= {}", key, value); + } + + logger.info("def loc: {}", rootDevice.getDeviceDefLoc()); + logger.trace("def loc data: {}", rootDevice.getDeviceDefLocData()); + logger.info("icons: {}", rootDevice.getDeviceIcons()); + logger.info("device type: {}", rootDevice.getDeviceType()); + logger.info("direct parent: {}", rootDevice.getDirectParent()); + logger.info("disc udn: {}", rootDevice.getDiscoveryUDN()); + logger.info("disc usn: {}", rootDevice.getDiscoveryUSN()); + logger.info("udn: {}", rootDevice.getUDN()); + } + + private boolean addPortMapping(final String description, final Protocol protocol, final String remoteHost, + final int externalPort, final String internalClient, final int internalPort, final int leaseDuration) + throws RouterException { + + final String protocolString = protocol == Protocol.TCP ? "TCP" : "UDP"; + + final String encodedDescription = encodeIfNecessary(description); + + try { + final boolean success = router.addPortMapping(encodedDescription, null, internalPort, externalPort, + internalClient, leaseDuration, protocolString); + return success; + } catch (final IOException e) { + throw new RouterException("Could not add port mapping: " + e.getMessage(), e); + } catch (final UPNPResponseException e) { + throw new RouterException("Could not add port mapping: " + e.getMessage(), e); + } + } + + private String encodeIfNecessary(final String description) { + final Settings settings = app.getSettings(); + if (settings == null || settings.isUseEntityEncoding()) { + return EncodingUtilities.htmlEntityEncode(description); + } + return description; + } + + @Override + public void addPortMappings(final Collection mappings) throws RouterException { + for (final PortMapping portMapping : mappings) { + logger.info("Adding port mapping {}", portMapping); + addPortMapping(portMapping); + } + } + + @Override + public void addPortMapping(final PortMapping mapping) throws RouterException { + logger.info("Adding port mapping ()", mapping.getCompleteDescription()); + addPortMapping(mapping.getDescription(), mapping.getProtocol(), mapping.getRemoteHost(), + mapping.getExternalPort(), mapping.getInternalClient(), mapping.getInternalPort(), 0); + } + + @Override + public void removeMapping(final PortMapping mapping) throws RouterException { + removePortMapping(mapping.getProtocol(), mapping.getRemoteHost(), mapping.getExternalPort()); + } + + @Override + public void removePortMapping(final Protocol protocol, final String remoteHost, final int externalPort) + throws RouterException { + final String protocolString = (protocol.equals(Protocol.TCP) ? "TCP" : "UDP"); + try { + router.deletePortMapping(remoteHost, externalPort, protocolString); + } catch (final IOException e) { + throw new RouterException("Could not remove port mapping", e); + } catch (final UPNPResponseException e) { + throw new RouterException("Could not remove port mapping", e); + } + } + + @Override + public void disconnect() { + // Nothing to do right now. + } + + public long getUpTime() { + // The SBBI library does not provide a method for getting the uptime. + return 0; + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouterFactory.java b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouterFactory.java new file mode 100644 index 0000000..7880e13 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/sbbi/SBBIRouterFactory.java @@ -0,0 +1,76 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.sbbi; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.sbbi.upnp.impls.InternetGatewayDevice; + +import org.chris.portmapper.PortMapperApp; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; + +/** + * Router factory using the SBBI UPnP library. + */ +public class SBBIRouterFactory extends AbstractRouterFactory { + + /** + * The timeout in milliseconds for finding a router device. + */ + private final static int DISCOVERY_TIMEOUT = 5000; + + public SBBIRouterFactory(final PortMapperApp app) { + super(app, "SBBI UPnP lib"); + } + + @Override + protected List findRoutersInternal() throws RouterException { + + final InternetGatewayDevice[] devices; + try { + devices = InternetGatewayDevice.getDevices(DISCOVERY_TIMEOUT); + } catch (final IOException e) { + throw new RouterException("Could not find devices", e); + } + + if (devices == null || devices.length == 0) { + return Collections.emptyList(); + } + + final List routers = new ArrayList<>(devices.length); + + for (final InternetGatewayDevice device : devices) { + routers.add(new SBBIRouter(app, device)); + } + + return routers; + } + + @Override + protected IRouter connect(final String locationUrl) throws RouterException { + throw new UnsupportedOperationException("Direct connection is not implemented for SBBI library."); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouter.java b/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouter.java new file mode 100644 index 0000000..58176d5 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouter.java @@ -0,0 +1,182 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.weupnp; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.bitlet.weupnp.GatewayDevice; +import org.bitlet.weupnp.PortMappingEntry; +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.model.Protocol; +import org.chris.portmapper.router.AbstractRouter; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is an implements an {@link IRouter} using the weupnp library's {@link GatewayDevice}. + */ +public class WeUPnPRouter extends AbstractRouter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final GatewayDevice device; + + WeUPnPRouter(final GatewayDevice device) { + super(device.getFriendlyName()); + this.device = device; + } + + @Override + public void addPortMapping(final PortMapping mapping) throws RouterException { + try { + device.addPortMapping(mapping.getExternalPort(), mapping.getInternalPort(), mapping.getInternalClient(), + mapping.getProtocol().getName(), mapping.getDescription()); + } catch (final Exception e) { + throw new RouterException("Could not add portmapping", e); + } + } + + @Override + public void addPortMappings(final Collection mappings) throws RouterException { + for (final PortMapping mapping : mappings) { + this.addPortMapping(mapping); + } + } + + @Override + public void disconnect() { + // noting to do right now + } + + @Override + public String getExternalIPAddress() throws RouterException { + try { + return device.getExternalIPAddress(); + } catch (final Exception e) { + throw new RouterException("Could not get external IP address", e); + } + } + + @Override + public String getInternalHostName() { + final String url = device.getPresentationURL(); + if (url == null || url.trim().length() == 0) { + return null; + } + try { + return new URL(url).getHost(); + } catch (final MalformedURLException e) { + logger.warn("Could not get URL for internal host name '" + url + "'", e); + return url; + } + } + + @Override + public int getInternalPort() throws RouterException { + String url = device.getPresentationURL(); + if (url == null) { + url = device.getURLBase(); + logger.info("Presentation url is null: use url base '{}'", url); + } + if (url == null) { + throw new RouterException("Presentation URL and URL base are null"); + } + + try { + return new URL(url).getPort(); + } catch (final MalformedURLException e) { + throw new RouterException("Could not get internal port from URL '" + url + "'", e); + } + } + + @Override + public Collection getPortMappings() throws RouterException { + final Collection mappings = new LinkedList<>(); + boolean morePortMappings = true; + int index = 0; + while (morePortMappings) { + final PortMappingEntry entry = new PortMappingEntry(); + try { + logger.debug("Getting port mapping {}...", index); + if (!device.getGenericPortMappingEntry(index, entry)) { + throw new RuntimeException(); + } + logger.debug("Got port mapping {}: {}", index, entry); + } catch (final Exception e) { + morePortMappings = false; + logger.debug("Got an exception with message '{}‘ for index {}, stop getting more mappings", + e.getMessage(), index); + } + + if (entry.getProtocol() != null) { + final Protocol protocol = entry.getProtocol().equalsIgnoreCase("TCP") ? Protocol.TCP : Protocol.UDP; + final PortMapping m = new PortMapping(protocol, entry.getRemoteHost(), entry.getExternalPort(), + entry.getInternalClient(), entry.getInternalPort(), entry.getPortMappingDescription()); + mappings.add(m); + } else { + logger.debug("Got null port mapping for index {}", index); + } + index++; + } + return mappings; + } + + @Override + public void logRouterInfo() throws RouterException { + final Map info = new HashMap<>(); + info.put("friendlyName", device.getFriendlyName()); + info.put("manufacturer", device.getManufacturer()); + info.put("modelDescription", device.getModelDescription()); + + final SortedSet sortedKeys = new TreeSet<>(info.keySet()); + + for (final String key : sortedKeys) { + final String value = info.get(key); + logger.info("Router Info: {} \t= {}", key, value); + } + + logger.info("def loc: {}", device.getLocation()); + logger.info("device type: {}", device.getDeviceType()); + } + + @Override + public void removeMapping(final PortMapping mapping) throws RouterException { + this.removePortMapping(mapping.getProtocol(), mapping.getRemoteHost(), mapping.getExternalPort()); + } + + @Override + public void removePortMapping(final Protocol protocol, final String remoteHost, final int externalPort) + throws RouterException { + try { + device.deletePortMapping(externalPort, protocol.getName()); + } catch (final Exception e) { + throw new RouterException("Could not delete port mapping", e); + } + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouterFactory.java b/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouterFactory.java new file mode 100644 index 0000000..3d992f3 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/router/weupnp/WeUPnPRouterFactory.java @@ -0,0 +1,92 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.router.weupnp; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.bitlet.weupnp.GatewayDevice; +import org.bitlet.weupnp.GatewayDiscover; +import org.chris.portmapper.PortMapperApp; +import org.chris.portmapper.router.AbstractRouterFactory; +import org.chris.portmapper.router.IRouter; +import org.chris.portmapper.router.RouterException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A router factoring using the weupnp library. + */ +public class WeUPnPRouterFactory extends AbstractRouterFactory { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final GatewayDiscover discover = new GatewayDiscover(); + + public WeUPnPRouterFactory(final PortMapperApp app) { + super(app, "weupnp lib"); + } + + @Override + protected List findRoutersInternal() throws RouterException { + logger.debug("Searching for gateway devices..."); + final Map devices; + try { + devices = discover.discover(); + } catch (final Exception e) { + throw new RouterException("Could not discover a valid gateway device: " + e.getMessage(), e); + } + + if (devices == null || devices.size() == 0) { + return Collections.emptyList(); + } + + final List routers = new ArrayList<>(devices.size()); + for (final GatewayDevice device : devices.values()) { + routers.add(new WeUPnPRouter(device)); + } + return routers; + } + + @Override + protected IRouter connect(final String locationUrl) throws RouterException { + + final GatewayDevice device = new GatewayDevice(); + device.setLocation(locationUrl); + device.setSt("urn:schemas-upnp-org:device:InternetGatewayDevice:1"); + try { + device.setLocalAddress(InetAddress.getLocalHost()); + } catch (final UnknownHostException e) { + throw new RouterException("Could not get ip of localhost: " + e.getMessage(), e); + } + try { + device.loadDescription(); + } catch (final Exception e) { + throw new RouterException( + "Could not load description of device for location url " + locationUrl + " : " + e.getMessage(), e); + } + return new WeUPnPRouter(device); + } +} diff --git a/UPnP/src/main/java/org/chris/portmapper/util/EncodingUtilities.java b/UPnP/src/main/java/org/chris/portmapper/util/EncodingUtilities.java new file mode 100644 index 0000000..a4e0998 --- /dev/null +++ b/UPnP/src/main/java/org/chris/portmapper/util/EncodingUtilities.java @@ -0,0 +1,61 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +/** + * + */ +package org.chris.portmapper.util; + +import java.util.HashMap; +import java.util.Map; + +public class EncodingUtilities { + + private static Map knownEncodings; + + static { + knownEncodings = new HashMap<>(); + knownEncodings.put('<', "<"); + knownEncodings.put('>', ">"); + knownEncodings.put('&', "&"); + } + + /** + * Replace all special characters with their html entities. + * + * @param s + * the string in which to replace the special characters. + * @return the result of the replacement. + */ + public static String htmlEntityEncode(final String s) { + final StringBuffer buf = new StringBuffer(); + + for (int i = 0; i < s.length(); i++) { + final char c = s.charAt(i); + if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9') { + buf.append(c); + } else { + if (knownEncodings.containsKey(c)) { + buf.append(knownEncodings.get(c)); + } else { + buf.append("&#" + (int) c + ";"); + } + } + } + return buf.toString(); + } +} diff --git a/UPnP/src/main/resources/logback.xml b/UPnP/src/main/resources/logback.xml new file mode 100644 index 0000000..baec989 --- /dev/null +++ b/UPnP/src/main/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_de.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_de.properties new file mode 100644 index 0000000..10b5f89 --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_de.properties @@ -0,0 +1,26 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +title = Meine Aufgabe +description = Verbinden zum Router. +startMessage = Verbinde... +errorMessage = Fehler: Verbindung zu %s konnte nicht aufgebaut werden +finishedMessage = Verbindung zu %s aufgebaut + +updateAddresses = Adressen werden aktualisiert +updatePortMappings = Port Mappings werden aktualisiert \ No newline at end of file diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_en.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_en.properties new file mode 100644 index 0000000..f8c2087 --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_en.properties @@ -0,0 +1,26 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +title = My Task +description = Connect to router. +startMessage = Connecting to router +errorMessage = Error: Could not connect to router: %s +finishedMessage = Connected to router %s + +updateAddresses = Updating Addresses +updatePortMappings = Updating Port Mappings \ No newline at end of file diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_nb.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_nb.properties new file mode 100644 index 0000000..17007da --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/ConnectTask_nb.properties @@ -0,0 +1,26 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +title = Min Oppgave +description = Koble til ruter. +startMessage = Kobler til ruter... +errorMessage = Feil: Kunne ikke koble til ruter: %s +finishedMessage = Koblet til ruter %s + +updateAddresses = Oppdaterer adresser +updatePortMappings = Oppdaterer portoppf�ringer \ No newline at end of file diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_de.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_de.properties new file mode 100644 index 0000000..c41e58a --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_de.properties @@ -0,0 +1,231 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +Application.id=PortMapper +Application.title=PortMapper +Application.version=@VERSION_NUMBER@ + +Application.lookAndFeel=system + +mainFrame.title=${Application.title} ${Application.version} + +messages.error_getting_localhost_address=Fehler beim Ermitteln der lokalen IP-Addresse.\nBenutzen Sie den 'Hinzuf�gen...' Button um eine Weiterleitung zu erzeugen\nund geben Sie ihre IP-Addresse manuell ein. +messages.error=Fehler + +messages.select_router.title=Router ausw�hlen +messages.select_router.message=Es wurde mehr als ein Router gefunden.\nBitte w�hlen Sie einen aus. + +mainFrame.router.title=Router +mainFrame.router.not_connected=(nicht verbunden) +mainFrame.router.updating=(aktualisieren...) +mainFrame.router.external_address=Externe IP-Addresse +mainFrame.router.internal_address=Interne IP-Addresse +mainFrame.log_messages.title=Log-Meldungen +mainFrame.port_mappings.title=Portweiterleitungen + + +mainFrame.mappings.protocol=Protokoll +mainFrame.mappings.remote_host=Entfernter Host +mainFrame.mappings.external_port=Externer Port +mainFrame.mappings.internal_client=Interne Addresse +mainFrame.mappings.internal_port=Interner Port +mainFrame.mappings.description=Beschreibung + +mainFrame.port_mapping_presets.title=Vorlagen + +# Actions + +mainFrame.showAboutDialog.Action.text=&�ber... +mainFrame.showAboutDialog.Action.shortDescription=Zeigt das �ber... Fenster + +mainFrame.router.updateAddresses.Action.text=&Aktualisieren +mainFrame.router.updateAddresses.Action.shortDescription=Aktualisiere die IP-Addressen des Router + +mainFrame.router.info.Action.text=&Info +mainFrame.router.info.Action.shortDescription=Zeige Informationen �ber den Router an + +mainFrame.router.connect.Action.text=&Verbinden +mainFrame.router.connect.Action.shortDescription=Verbindung zum Router aufbauen + +mainFrame.router.disconnect.Action.text=&Trennen +mainFrame.router.disconnect.Action.shortDescription=Verbindung zum Router trennen + +mainFrame.router.copyInternalAddress.Action.text=Kopieren +mainFrame.router.copyInternalAddress.Action.shortDescription=Kopiere die interne Addresse des Routers in die Zwischenablage + +mainFrame.router.copyExternalAddress.Action.text=Kopieren +mainFrame.router.copyExternalAddress.Action.shortDescription=Kopiere die externe Addresse des Routers in die Zwischenablage + +mainFrame.mappings.update.Action.text=Aktualisiere +mainFrame.mappings.update.Action.shortDescription=Aktualisiere die Portweiterleitungen + +mainFrame.mappings.remove.Action.text=&Entfernen +mainFrame.mappings.remove.Action.shortDescription=Entferne die ausgew�hlte Portweiterleitungen + +mainFrame.preset_mappings.create.Action.text=&Hinzuf�gen... +mainFrame.preset_mappings.create.Action.shortDescription=F�ge eine neue Portweiterleitung hinzu + +mainFrame.preset_mappings.edit.Action.text=Bearbeiten +mainFrame.preset_mappings.edit.Action.shortDescription=Bearbeite die ausgew�hlte Vorlage + +mainFrame.preset_mappings.remove.Action.text=&L�schen +mainFrame.preset_mappings.remove.Action.shortDescription=L�sche die ausgew�hlte Vorlage + +mainFrame.preset_mappings.use.Action.text=&Benutze +mainFrame.preset_mappings.use.Action.shortDescription=Benutze die ausgew�hlte Vorlage + +mainFrame.portmapper.settings.Action.text=Einstellungen... +mainFrame.portmapper.settings.Action.shortDescription=Einstellungen von PortMapper �ndern + +# About dialog + +about_dialog.close.Action.text=Schlie�en +about_dialog.close.Action.shortDescription=Schlie�e den Dialog + +about_dialog.title=�ber ${Application.title} ${Application.version} + +about_dialog.label1.text=${Application.title} Version ${Application.version} +about_dialog.label2.text=Verwalten der Portweiterleitungen eines Routers mit Universal Plug and Play. +about_dialog.label3.text=Created by Christoph Pirkl +about_dialog.label4.text=Folgende Bibiliotheken wurden benutzt + + +about_dialog.tooltip.click_here=Hier klicken um die URL +about_dialog.tooltip.in_browser=in einem Browser zu �ffnen + +about_dialog.upnplib_label.label=SBBI UPNPLib +about_dialog.upnplib_label.url=https://sourceforge.net/projects/upnplibmobile/ +about_dialog.upnplib_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.upnplib_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.weupnp_label.label=weupnp +about_dialog.weupnp_label.url=http://code.google.com/p/weupnp/ +about_dialog.weupnp_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.weupnp_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.cling_label.label=Cling +about_dialog.cling_label.url=http://4thline.org/projects/cling/ +about_dialog.cling_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.cling_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.app_framework_label.label=Better Swing Application Framework +about_dialog.app_framework_label.url=https://kenai.com/projects/bsaf/pages/Home +about_dialog.app_framework_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.app_framework_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.slf4j_label.label=slf4j +about_dialog.slf4j_label.url=http://www.slf4j.org/ +about_dialog.slf4j_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.slf4j_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.logback_label.label=logback +about_dialog.logback_label.url=http://logback.qos.ch/ +about_dialog.logback_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.logback_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.miglayout_label.label=MiGLayout +about_dialog.miglayout_label.url=http://www.miglayout.com/ +about_dialog.miglayout_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.miglayout_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.label5.text=Besuchen Sie die ${Application.title} Homepage + +about_dialog.homepage_label.label=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.url=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.homepage_label.url} ${about_dialog.tooltip.in_browser} + +# Edit preset mapping dialog + + +preset_dialog.title=Bearbeiten der Vorlagen + +preset_dialog.add_port.Action.text=Hinzuf�gen +preset_dialog.add_port.Action.shortDescription=F�ge einen neuen Port zur Liste hinzu + +preset_dialog.add_port_range.Action.text=Bereich hinzuf�gen... +preset_dialog.add_port_range.Action.shortDescription=F�ge mehrere Ports zur Liste hinzu + +preset_dialog.remove_port.Action.text=Entfernen +preset_dialog.remove_port.Action.shortDescription=Entferne einen Port aus der Liste + +preset_dialog.save.Action.text=Speichern +preset_dialog.save.Action.shortDescription=Speichere die �nderungen an den Vorlagen + +preset_dialog.cancel.Action.text=Abbrechen +preset_dialog.cancel.Action.shortDescription=Die �nderungen an den Vorlagen nicht speichern + +preset_dialog.ports.title=Ports + +preset_dialog.ports.protocol=Protokoll +preset_dialog.ports.internal=Interner Port +preset_dialog.ports.external=Externer Port + +preset_dialog.preset.text=Vorlagen +preset_dialog.protocol.text=Protokoll +preset_dialog.description.text=Beschreibung +preset_dialog.protocol_tcp.text=TCP +preset_dialog.protocol_udp.text=UDP +preset_dialog.remote_host.text=Entfernter Host +preset_dialog.remote_host_empty_for_all.text=(leer f�r alle) +preset_dialog.external_port.text=Externer Port +preset_dialog.internal_client.text=Interner Client +preset_dialog.internal_client_use_local_host.text=Benutze localhost +preset_dialog.internal_port.text=Interner Port + +preset_dialog.error.title=Fehler +preset_dialog.error.no_description=Bitte eine Beschreibung eingeben. +preset_dialog.error.no_ports=Bitte mindestens einen Port hinzuf�gen. +preset_dialog.error.duplicate_name=Eine Vorlage mit dieser Beschreibung existiert bereits, bitte eine andere Beschreibung eingeben. + +# Add port range dialog + +add_port_range_dialog.title=Mehrere Ports hinzuf�gen + +add_port_range_dialog.cancel.Action.text=Abbrechen +add_port_range_dialog.cancel.Action.shortDescription=Ports nicht hinzuf�gen + +add_port_range_dialog.add.Action.text=Hinzuf�gen +add_port_range_dialog.add.Action.shortDescription=Ports zur Liste hinzuf�gen + +add_port_range_dialog.protocol.text=Protokoll +add_port_range_dialog.internal_ports_from.text=Interne Ports: +add_port_range_dialog.internal_ports_to.text=bis +add_port_range_dialog.external_ports_from.text=Externe Ports: +add_port_range_dialog.external_ports_to.text=bis + +add_port_range_dialog.external_equal_internal.text=Externe Ports sind identisch mit internen Ports +add_port_range_dialog.invalid_number.title=Ung�ltige Port-Nummer +add_port_range_dialog.invalid_number.message=Bitte geben Sie Zahlen von 1 bis 65535 ein. + +add_port_range_dialog.invalid_internal_range.title=Ung�ltiger Zahlenbereich f�r internen Port +add_port_range_dialog.invalid_internal_range.message=Der 'bis'-Port muss gr��er als der 'von'-Port sein. + +add_port_range_dialog.invalid_external_range.title=Ung�ltiger Zahlenbereich f�r externen Port +add_port_range_dialog.invalid_external_range.message=Der 'bis'-Port muss gr��er als der 'von'-Port sein. + +add_port_range_dialog.invalid_range_length.title=Ung�ltiger Bereich +add_port_range_dialog.invalid_range_length.message=Der Bereich der externen Ports muss genauso lang sein wie der Bereich der internen Ports. + + +# Settings dialog + +settings_dialog.title=UPnP PortMapper Einstellungen + +settings_dialog.save.Action.text=Speichern +settings_dialog.save.Action.shortDescription=Einstellungen speichern + +settings_dialog.cancel.Action.text=Abbrechen +settings_dialog.cancel.Action.shortDescription=Einstellungen nicht speichern + +settings_dialog.use_entity_encoding.text=Verwende Entity Encoding f�r Beschreibungen + +settings_dialog.log_level.text=Log Granularit�t +settings_dialog.upnp_lib.text=UPnP Bibliothek diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_en.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_en.properties new file mode 100644 index 0000000..12ef40f --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_en.properties @@ -0,0 +1,230 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +Application.id=PortMapper +Application.title=PortMapper +Application.version=@VERSION_NUMBER@ + +Application.lookAndFeel=system + +mainFrame.title=${Application.title} ${Application.version} + +messages.error_getting_localhost_address=Could not retrieve address of local host.\nPlease use 'Add...' button to create a mapping\nand enter address of local host manually. +messages.error=Error + +messages.select_router.title=Select router +messages.select_router.message=More than one router was found.\nPlease select one to connect to. + +mainFrame.router.title=Router +mainFrame.router.not_connected=(not connected) +mainFrame.router.updating=(updating) +mainFrame.router.external_address=External address +mainFrame.router.internal_address=Internal address +mainFrame.log_messages.title=Log messages +mainFrame.port_mappings.title=Port mappings + + +mainFrame.mappings.protocol=Protocol +mainFrame.mappings.remote_host=Remote Host +mainFrame.mappings.external_port=External Port +mainFrame.mappings.internal_client=Internal Client +mainFrame.mappings.internal_port=Internal Port +mainFrame.mappings.description=Description + +mainFrame.port_mapping_presets.title=Port mapping presets + +# Actions + +mainFrame.showAboutDialog.Action.text=&About... +mainFrame.showAboutDialog.Action.shortDescription=Show about dialog + +mainFrame.router.updateAddresses.Action.text=&Update +mainFrame.router.updateAddresses.Action.shortDescription=Update router addresses + +mainFrame.router.info.Action.text=&Info +mainFrame.router.info.Action.shortDescription=Display router information + +mainFrame.router.connect.Action.text=&Connect +mainFrame.router.connect.Action.shortDescription=Connect to router + +mainFrame.router.disconnect.Action.text=&Disconnect +mainFrame.router.disconnect.Action.shortDescription=Disconnect from router + +mainFrame.router.copyInternalAddress.Action.text=Copy +mainFrame.router.copyInternalAddress.Action.shortDescription=Copy internal address of router to clipboard + +mainFrame.router.copyExternalAddress.Action.text=Copy +mainFrame.router.copyExternalAddress.Action.shortDescription=Copy external address of router to clipboard + +mainFrame.mappings.update.Action.text=Update +mainFrame.mappings.update.Action.shortDescription=Update port mappings + +mainFrame.mappings.remove.Action.text=&Remove +mainFrame.mappings.remove.Action.shortDescription=Remove selected port mappings + +mainFrame.preset_mappings.create.Action.text=Create +mainFrame.preset_mappings.create.Action.shortDescription=Create a new port mapping preset + +mainFrame.preset_mappings.edit.Action.text=Edit +mainFrame.preset_mappings.edit.Action.shortDescription=Edit the selected port mapping preset + +mainFrame.preset_mappings.remove.Action.text=Delete +mainFrame.preset_mappings.remove.Action.shortDescription=Delete the selected port mapping preset + +mainFrame.preset_mappings.use.Action.text=Use +mainFrame.preset_mappings.use.Action.shortDescription=Use the selected port mapping preset + +mainFrame.portmapper.settings.Action.text=PortMapper Settings... +mainFrame.portmapper.settings.Action.shortDescription=Edit the settings of UPnP PortMapper + + +# About dialog + +about_dialog.close.Action.text=Close +about_dialog.close.Action.shortDescription=Close about dialog + +about_dialog.title=About ${Application.title} ${Application.version} + +about_dialog.label1.text=${Application.title} Version ${Application.version} +about_dialog.label2.text=Manage port mappings of a router using Universal Plug and Play. +about_dialog.label3.text=Created by Christoph Pirkl +about_dialog.label4.text=Using the following libraries + + +about_dialog.tooltip.click_here=Click here to open URL +about_dialog.tooltip.in_browser=in an external browser + +about_dialog.upnplib_label.label=SBBI UPNPLib +about_dialog.upnplib_label.url=https://sourceforge.net/projects/upnplibmobile/ +about_dialog.upnplib_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.upnplib_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.weupnp_label.label=weupnp +about_dialog.weupnp_label.url=http://code.google.com/p/weupnp/ +about_dialog.weupnp_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.weupnp_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.cling_label.label=Cling +about_dialog.cling_label.url=http://4thline.org/projects/cling/ +about_dialog.cling_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.cling_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.app_framework_label.label=Better Swing Application Framework +about_dialog.app_framework_label.url=https://kenai.com/projects/bsaf/pages/Home +about_dialog.app_framework_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.app_framework_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.slf4j_label.label=slf4j +about_dialog.slf4j_label.url=http://www.slf4j.org/ +about_dialog.slf4j_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.slf4j_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.logback_label.label=logback +about_dialog.logback_label.url=http://logback.qos.ch/ +about_dialog.logback_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.logback_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.miglayout_label.label=MiGLayout +about_dialog.miglayout_label.url=http://www.miglayout.com/ +about_dialog.miglayout_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.miglayout_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.label5.text=Visit our homepage at + +about_dialog.homepage_label.label=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.url=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.homepage_label.url} ${about_dialog.tooltip.in_browser} + +# Edit preset mapping dialog + +preset_dialog.title=Edit Port Mapping Preset + +preset_dialog.add_port.Action.text=Add +preset_dialog.add_port.Action.shortDescription=Add a new port to the list + +preset_dialog.add_port_range.Action.text=Add port range... +preset_dialog.add_port_range.Action.shortDescription=Add a range of ports to the list + +preset_dialog.remove_port.Action.text=Remove +preset_dialog.remove_port.Action.shortDescription=Remove selected port from the list + +preset_dialog.save.Action.text=Save +preset_dialog.save.Action.shortDescription=Save the changed port mapping preset + +preset_dialog.cancel.Action.text=Cancel +preset_dialog.cancel.Action.shortDescription=Do not save the changed port mapping preset + +preset_dialog.ports.title=Ports + +preset_dialog.ports.protocol=Protocol +preset_dialog.ports.internal=Internal Port +preset_dialog.ports.external=External Port + +preset_dialog.preset.text=Preset +preset_dialog.protocol.text=Protocol +preset_dialog.description.text=Description +preset_dialog.protocol_tcp.text=TCP +preset_dialog.protocol_udp.text=UDP +preset_dialog.remote_host.text=Remote Host +preset_dialog.remote_host_empty_for_all.text=(empty for all) +preset_dialog.external_port.text=External Port +preset_dialog.internal_client.text=Internal Client +preset_dialog.internal_client_use_local_host.text=Use local host +preset_dialog.internal_port.text=Internal Port + +preset_dialog.error.title=Error +preset_dialog.error.no_description=Please enter a description. +preset_dialog.error.no_ports=Please add at least one port mapping. +preset_dialog.error.duplicate_name=A preset with this already exists, please enter another description. + +# Add port range dialog + +add_port_range_dialog.title=Add port range + +add_port_range_dialog.cancel.Action.text=Cancel +add_port_range_dialog.cancel.Action.shortDescription=Do not add the port range + +add_port_range_dialog.add.Action.text=Add +add_port_range_dialog.add.Action.shortDescription=Add the port range to the list + +add_port_range_dialog.protocol.text=Protocol +add_port_range_dialog.internal_ports_from.text=Internal ports: +add_port_range_dialog.internal_ports_to.text=to +add_port_range_dialog.external_ports_from.text=External ports: +add_port_range_dialog.external_ports_to.text=to + +add_port_range_dialog.external_equal_internal.text=External ports are equal to internal ports +add_port_range_dialog.invalid_number.title=Invalid port number +add_port_range_dialog.invalid_number.message=Please enter numbers from 1 to 65535. + +add_port_range_dialog.invalid_internal_range.title=Invalid internal port range +add_port_range_dialog.invalid_internal_range.message=The 'to' internal port must be higher than the 'from' internal port. + +add_port_range_dialog.invalid_external_range.title=Invalid external port range +add_port_range_dialog.invalid_external_range.message=The 'to' external port must be higher than the 'from' external port. + +add_port_range_dialog.invalid_range_length.title=Invalid range length +add_port_range_dialog.invalid_range_length.message=The external port range must contain the same number of ports\nas the internal port range. + +# Settings dialog + +settings_dialog.title=UPnP PortMapper Settings + +settings_dialog.save.Action.text=Save +settings_dialog.save.Action.shortDescription=Save the settings + +settings_dialog.cancel.Action.text=Cancel +settings_dialog.cancel.Action.shortDescription=Do not save the settings + +settings_dialog.use_entity_encoding.text=Use entity encoding for port mapping names + +settings_dialog.log_level.text=Log level +settings_dialog.upnp_lib.text=UPnP library diff --git a/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_nb.properties b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_nb.properties new file mode 100644 index 0000000..6f79953 --- /dev/null +++ b/UPnP/src/main/resources/org/chris/portmapper/resources/PortMapperApp_nb.properties @@ -0,0 +1,177 @@ +# +# UPnP PortMapper - A tool for managing port forwardings via UPnP +# Copyright (C) 2015 Christoph Pirkl +# +# 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 3 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, see . +# + +Application.id=PortMapper +Application.title=PortMapper +Application.version=@VERSION_NUMBER@ + +Application.lookAndFeel=system + +mainFrame.title=${Application.title} ${Application.version} + +messages.error_getting_localhost_address=Fant ikke adressen til den lokal verten.\nTrykk p� "Legg til"-knappen for � opprette en oppf�ring\nog manuelt legge in adressen til den lokale verten. +messages.error=Feil + +mainFrame.router.title=Ruter +mainFrame.router.not_connected=(ikke tilkoblet) +mainFrame.router.updating=(oppdaterer) +mainFrame.router.external_address=Ekstern adresse +mainFrame.router.internal_address=Intern adresse +mainFrame.log_messages.title=Loggf�rte beskjeder +mainFrame.port_mappings.title=Portoppf�ringer + + +mainFrame.mappings.protocol=Protokoll +mainFrame.mappings.remote_host=Ekstern vert +mainFrame.mappings.external_port=Ekstern port +mainFrame.mappings.internal_client=Intern klient +mainFrame.mappings.internal_port=Intern port +mainFrame.mappings.description=Beskrivelse + +mainFrame.port_mapping_presets.title=Portoppf�ringsmaler + +# Actions + +mainFrame.showAboutDialog.Action.text=&Om... +mainFrame.showAboutDialog.Action.shortDescription=Vis Om vinduet + +mainFrame.router.updateAddresses.Action.text=&Oppdater +mainFrame.router.updateAddresses.Action.shortDescription=Oppdater ruter adressen + +mainFrame.router.info.Action.text=&Info +mainFrame.router.info.Action.shortDescription=Vis ruter informasjon + +mainFrame.router.connect.Action.text=&Koble til +mainFrame.router.connect.Action.shortDescription=Koble til ruter + +mainFrame.router.disconnect.Action.text=&Koble fra +mainFrame.router.disconnect.Action.shortDescription=Koble fra ruter + +mainFrame.router.copyInternalAddress.Action.text=Kopier +mainFrame.router.copyInternalAddress.Action.shortDescription=Kopier ruterens interne adresse til utklippstavlen + +mainFrame.router.copyExternalAddress.Action.text=Kopier +mainFrame.router.copyExternalAddress.Action.shortDescription=Kopier ruterens Eksterne adresse til utklippstavlen + +mainFrame.mappings.update.Action.text=Oppdater +mainFrame.mappings.update.Action.shortDescription=Oppdater portoppf�ringer + +mainFrame.mappings.remove.Action.text=&Fjern +mainFrame.mappings.remove.Action.shortDescription=Fjern valgte portoppf�ringer + +mainFrame.preset_mappings.create.Action.text=Opprett +mainFrame.preset_mappings.create.Action.shortDescription=Opprett en ny portoppf�ringsmal + +mainFrame.preset_mappings.edit.Action.text=Rediger +mainFrame.preset_mappings.edit.Action.shortDescription=Rediger valgt portoppf�ringsmal + +mainFrame.preset_mappings.remove.Action.text=Slett +mainFrame.preset_mappings.remove.Action.shortDescription=Slett valgt portoppf�ringsmal + +mainFrame.preset_mappings.use.Action.text=Bruk +mainFrame.preset_mappings.use.Action.shortDescription=Bruk valgt portoppf�ringsmal + + +# About dialog + +about_dialog.close.Action.text=Lukk +about_dialog.close.Action.shortDescription=Lukk Om vinduet + +about_dialog.title=Om ${Application.title} ${Application.version} + +about_dialog.label1.text=${Application.title} versjon ${Application.version} +about_dialog.label2.text=Administer en ruters portoppf�ringer ved bruk av Universal Plug and Play +about_dialog.label3.text=Utviklet av Christoph Pirkl +about_dialog.label4.text=F�lgene biblioteker er i bruk + + +about_dialog.tooltip.click_here=Klikk her for � �pne adressen +about_dialog.tooltip.in_browser=i nettleseren + +about_dialog.upnplib_label.label=SBBI UPNPLib +about_dialog.upnplib_label.url=https://sourceforge.net/projects/upnplibmobile/ +about_dialog.upnplib_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.upnplib_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.weupnp_label.label=weupnp +about_dialog.weupnp_label.url=http://code.google.com/p/weupnp/ +about_dialog.weupnp_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.weupnp_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.cling_label.label=Cling +about_dialog.cling_label.url=http://4thline.org/projects/cling/ +about_dialog.cling_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.cling_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.app_framework_label.label=Better Swing Application Framework +about_dialog.app_framework_label.url=https://kenai.com/projects/bsaf/pages/Home +about_dialog.app_framework_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.app_framework_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.slf4j_label.label=slf4j +about_dialog.slf4j_label.url=http://www.slf4j.org/ +about_dialog.slf4j_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.slf4j_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.logback_label.label=logback +about_dialog.logback_label.url=http://logback.qos.ch/ +about_dialog.logback_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.logback_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.miglayout_label.label=MiGLayout +about_dialog.miglayout_label.url=http://www.miglayout.com/ +about_dialog.miglayout_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.miglayout_label.url} ${about_dialog.tooltip.in_browser} + +about_dialog.label5.text=Bes�k v�r hjemmeside p� + +about_dialog.homepage_label.label=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.url=https://github.com/kaklakariada/portmapper +about_dialog.homepage_label.toolTipText=${about_dialog.tooltip.click_here} ${about_dialog.homepage_label.url} ${about_dialog.tooltip.in_browser} + +# Edit preset mapping dialog + + +preset_dialog.title=Rediger portoppf�ringsmal + +preset_dialog.add_port.Action.text=Legg til +preset_dialog.add_port.Action.shortDescription=Legg til en ny port til listen + +preset_dialog.remove_port.Action.text=Fjern +preset_dialog.remove_port.Action.shortDescription=Fjern valgt port fra listen + + +preset_dialog.save.Action.text=Lagre +preset_dialog.save.Action.shortDescription=Lagre endringene i portoppf�ringsmalen + +preset_dialog.cancel.Action.text=Avbryt +preset_dialog.cancel.Action.shortDescription=Forkast forandringene gjort til portoppf�ringsmalen + +preset_dialog.ports.title=Porter + +preset_dialog.ports.protocol=Protokoll +preset_dialog.ports.internal=Intern port +preset_dialog.ports.external=Ekstern port + +preset_dialog.preset.text=Mal +preset_dialog.protocol.text=Protokoll +preset_dialog.description.text=Beskrivelse +preset_dialog.protocol_tcp.text=TCP +preset_dialog.protocol_udp.text=UDP +preset_dialog.remote_host.text=Ekstern vert +preset_dialog.remote_host_empty_for_all.text=(la st� tom for alle) +preset_dialog.external_port.text=Ekstern port +preset_dialog.internal_client.text=Intern klient +preset_dialog.internal_client_use_local_host.text=Bruk den lokale verten +preset_dialog.internal_port.text=Intern port + + +preset_dialog.error.title=Feil diff --git a/UPnP/src/test/java/org/chris/portmapper/router/sbbi/TestPortMappingExtractor.java b/UPnP/src/test/java/org/chris/portmapper/router/sbbi/TestPortMappingExtractor.java new file mode 100644 index 0000000..97f5a82 --- /dev/null +++ b/UPnP/src/test/java/org/chris/portmapper/router/sbbi/TestPortMappingExtractor.java @@ -0,0 +1,138 @@ +/** + * UPnP PortMapper - A tool for managing port forwardings via UPnP + * Copyright (C) 2015 Christoph Pirkl + * + * 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 3 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, see . + */ +package org.chris.portmapper.router.sbbi; + +import static java.util.Arrays.*; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; + +import java.io.IOException; +import java.util.HashSet; + +import org.chris.portmapper.model.PortMapping; +import org.chris.portmapper.router.RouterException; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; + +import static org.mockito.Mockito.*; + +import net.sbbi.upnp.impls.InternetGatewayDevice; +import net.sbbi.upnp.messages.ActionResponse; +import net.sbbi.upnp.messages.UPNPResponseException; + +/** + * Unit tests for {@link SBBIPortMappingExtractor}. + */ +public class TestPortMappingExtractor { + + @Mock + private InternetGatewayDevice routerMock; + @Mock + private Logger loggerMock; + private SBBIPortMappingExtractor portMappingExtractor; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + portMappingExtractor = new SBBIPortMappingExtractor(routerMock, 5, loggerMock); + } + + @Test + public void allMappingsNull() throws RouterException, IOException, UPNPResponseException { + simulateUPNPException(5, 713); + assertEquals(0, portMappingExtractor.getPortMappings().size()); + verify(loggerMock, times(1)).warn(anyString(), anyInt()); + verify(loggerMock, never()).error(anyString()); + assertNumMappingsFound(0, 5); + } + + @Test + public void allMappingsNullMaxNumReached() throws RouterException { + assertEquals(0, portMappingExtractor.getPortMappings().size()); + verify(loggerMock, times(1)).warn(anyString(), anyInt()); + verify(loggerMock, never()).error(anyString()); + assertNumMappingsFound(0, 5); + } + + @Test + public void noMapping() throws RouterException, IOException, UPNPResponseException { + simulateUPNPException(0, 713); + assertEquals(0, portMappingExtractor.getPortMappings().size()); + assertNoWarningOrErrorLogged(); + assertNumMappingsFound(0, 0); + } + + @Test + public void wrongErrorCode() throws RouterException, IOException, UPNPResponseException { + simulateUPNPException(0, 42); + assertEquals(0, portMappingExtractor.getPortMappings().size()); + verify(loggerMock, never()).warn(anyString()); + verify(loggerMock, never()).error(anyString()); + verify(loggerMock, never()).warn(anyString(), any(Throwable.class)); + verify(loggerMock).error(anyString(), any(Throwable.class)); + assertNumMappingsFound(0, 0); + } + + @Test + public void oneMapping() throws RouterException, IOException, UPNPResponseException { + simulateMapping(0); + simulateUPNPException(1, 713); + assertEquals(1, portMappingExtractor.getPortMappings().size()); + assertNoWarningOrErrorLogged(); + assertNumMappingsFound(1, 0); + } + + private void assertNumMappingsFound(final int numFound, final int numNull) { + verify(loggerMock).debug("Found {} mappings, {} mappings returned as null.", numFound, numNull); + } + + private void assertNoWarningOrErrorLogged() { + verify(loggerMock, never()).warn(anyString()); + verify(loggerMock, never()).error(anyString()); + verify(loggerMock, never()).warn(anyString(), any(Throwable.class)); + verify(loggerMock, never()).error(anyString(), any(Throwable.class)); + } + + private void simulateMapping(final int mappingEntry) throws IOException, UPNPResponseException { + final ActionResponse response = mock(ActionResponse.class); + when(response.getOutActionArgumentNames()).thenReturn( + new HashSet(asList(PortMapping.MAPPING_ENTRY_ENABLED, PortMapping.MAPPING_ENTRY_EXTERNAL_PORT, + PortMapping.MAPPING_ENTRY_INTERNAL_CLIENT, PortMapping.MAPPING_ENTRY_INTERNAL_PORT, + PortMapping.MAPPING_ENTRY_LEASE_DURATION, PortMapping.MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION, + PortMapping.MAPPING_ENTRY_PROTOCOL, PortMapping.MAPPING_ENTRY_REMOTE_HOST))); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_ENABLED)).thenReturn("1"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_EXTERNAL_PORT)).thenReturn("2"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_INTERNAL_CLIENT)).thenReturn("internal"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_INTERNAL_PORT)).thenReturn("3"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_LEASE_DURATION)).thenReturn("4"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION)) + .thenReturn("description"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_PROTOCOL)).thenReturn("TCP"); + when(response.getOutActionArgumentValue(PortMapping.MAPPING_ENTRY_REMOTE_HOST)).thenReturn("remote"); + when(routerMock.getGenericPortMappingEntry(mappingEntry)).thenReturn(response); + } + + private void simulateUPNPException(final int mappingEntry, final int errorCode) + throws IOException, UPNPResponseException { + when(routerMock.getGenericPortMappingEntry(mappingEntry)).thenThrow(new UPNPResponseException(errorCode, + "exception for entry " + mappingEntry + ", error code " + errorCode)); + } +}